[{"id":19273,"web_url":"https://patchwork.libcamera.org/comment/19273/","msgid":"<65e06923-3532-432d-3f13-21044379d83a@ideasonboard.com>","date":"2021-09-02T08:49:52","subject":"Re: [libcamera-devel] [RFC PATCH v1 03/12] libcamera: formats: Add\n\tplaneSize() helpers to PixelFormatInfo","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:22, Laurent Pinchart wrote:\n> Add two helpers functions to the PixelFormatInfo class to compute the\n> byte size of a given plane, taking the frame size, the stride, the\n> alignement constraints and the vertical subsampling into account.\n\ns/alignement/alignment/\n\n\n> Use the new functions through the code base to replace manual\n> implementations.\n\n\n\\o/ that makes me happy.\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/internal/formats.h     |  4 ++\n>  src/android/mm/generic_camera_buffer.cpp | 11 +---\n>  src/android/yuv/post_processor_yuv.cpp   | 10 ++-\n>  src/libcamera/formats.cpp                | 82 ++++++++++++++++++++----\n>  src/libcamera/v4l2_videodevice.cpp       |  6 +-\n>  5 files changed, 79 insertions(+), 34 deletions(-)\n> \n> diff --git a/include/libcamera/internal/formats.h b/include/libcamera/internal/formats.h\n> index 51a8a6b8b0ae..8f314e99889b 100644\n> --- a/include/libcamera/internal/formats.h\n> +++ b/include/libcamera/internal/formats.h\n> @@ -42,6 +42,10 @@ public:\n>  \n>  \tunsigned int stride(unsigned int width, unsigned int plane,\n>  \t\t\t    unsigned int align = 1) const;\n> +\tunsigned int planeSize(const Size &size, unsigned int plane,\n> +\t\t\t       unsigned int align = 1) const;\n> +\tunsigned int planeSize(unsigned int height, unsigned int plane,\n> +\t\t\t       unsigned int stride) const;\n>  \tunsigned int frameSize(const Size &size, unsigned int align = 1) const;\n>  \tunsigned int frameSize(const Size &size,\n>  \t\t\t       const std::array<unsigned int, 3> &strides) const;\n> diff --git a/src/android/mm/generic_camera_buffer.cpp b/src/android/mm/generic_camera_buffer.cpp\n> index 22efc4d4b13a..93aa5821e470 100644\n> --- a/src/android/mm/generic_camera_buffer.cpp\n> +++ b/src/android/mm/generic_camera_buffer.cpp\n> @@ -108,16 +108,9 @@ CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer,\n>  \n>  \tunsigned int offset = 0;\n>  \tfor (unsigned int i = 0; i < numPlanes; ++i) {\n> -\t\t/*\n> -\t\t * \\todo Remove if this plane size computation function is\n> -\t\t * added to PixelFormatInfo.\n> -\t\t */\n> -\t\tconst unsigned int vertSubSample = info.planes[i].verticalSubSampling;\n> -\t\tconst unsigned int stride = info.stride(size.width, i, 1u);\n> -\t\tconst unsigned int planeSize =\n> -\t\t\tstride * ((size.height + vertSubSample - 1) / vertSubSample);\n> +\t\tconst unsigned int planeSize = info.planeSize(size, i);\n>  \n> -\t\tplaneInfo_[i].stride = stride;\n> +\t\tplaneInfo_[i].stride = info.stride(size.width, i, 1u);\n>  \t\tplaneInfo_[i].offset = offset;\n>  \t\tplaneInfo_[i].size = planeSize;\n>  \n> diff --git a/src/android/yuv/post_processor_yuv.cpp b/src/android/yuv/post_processor_yuv.cpp\n> index 6952fc38b0ef..7b3b49609cb1 100644\n> --- a/src/android/yuv/post_processor_yuv.cpp\n> +++ b/src/android/yuv/post_processor_yuv.cpp\n> @@ -134,11 +134,9 @@ void PostProcessorYuv::calculateLengths(const StreamConfiguration &inCfg,\n>  \t\tsourceStride_[i] = inCfg.stride;\n>  \t\tdestinationStride_[i] = nv12Info.stride(destinationSize_.width, i, 1);\n>  \n> -\t\tconst unsigned int vertSubSample =\n> -\t\t\tnv12Info.planes[i].verticalSubSampling;\n> -\t\tsourceLength_[i] = sourceStride_[i] *\n> -\t\t\t((sourceSize_.height + vertSubSample - 1) / vertSubSample);\n> -\t\tdestinationLength_[i] = destinationStride_[i] *\n> -\t\t\t((destinationSize_.height + vertSubSample - 1) / vertSubSample);\n> +\t\tsourceLength_[i] = nv12Info.planeSize(sourceSize_.height, i,\n> +\t\t\t\t\t\t      sourceStride_[i]);\n> +\t\tdestinationLength_[i] = nv12Info.planeSize(destinationSize_.height, i,\n> +\t\t\t\t\t\t\t   destinationStride_[i]);\n>  \t}\n>  }\n> diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp\n> index 603d88619fe0..76be93bc1c5c 100644\n> --- a/src/libcamera/formats.cpp\n> +++ b/src/libcamera/formats.cpp\n> @@ -11,6 +11,7 @@\n>  #include <errno.h>\n>  \n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/utils.h>\n>  \n>  #include <libcamera/formats.h>\n>  \n> @@ -801,32 +802,85 @@ unsigned int PixelFormatInfo::stride(unsigned int width, unsigned int plane,\n>  }\n>  \n>  /**\n> - * \\brief Compute the number of bytes necessary to store a frame\n> + * \\brief Compute the number of bytes necessary to store a plane of a frame\n>   * \\param[in] size The size of the frame, in pixels\n> + * \\param[in] plane The plane index\n>   * \\param[in] align The stride alignment, in bytes (1 for default alignment)\n>   *\n> - * The frame is computed by adding the product of the line stride and the frame\n> - * height for all planes, taking subsampling and other format characteristics\n> - * into account. Additional stride alignment constraints may be specified\n> - * through the \\a align parameter, and will apply to all planes. For more\n> - * complex stride constraints, use the frameSize() overloaded version that takes\n> - * an array of stride values.\n> + * The plane size is computed by multiplying the line stride and the frame\n> + * height, taking subsampling and other format characteristics into account.\n> + * Stride alignment constraints may be specified through the \\a align parameter.\n>   *\n>   * \\sa stride()\n>   *\n> + * \\return The number of bytes necessary to store the plane, or 0 if the\n> + * PixelFormatInfo instance is not valid or the plane number isn't valid for the\n> + * format\n> + */\n> +unsigned int PixelFormatInfo::planeSize(const Size &size, unsigned int plane,\n> +\t\t\t\t\tunsigned int align) const\n> +{\n> +\tunsigned int stride = PixelFormatInfo::stride(size.width, plane, align);\n> +\tif (!stride)\n> +\t\treturn 0;\n> +\n> +\tunsigned int vertSubSample = planes[plane].verticalSubSampling;\n> +\tif (!vertSubSample)\n> +\t\treturn 0;\n> +\n> +\t/* stride * ceil(height / verticalSubSampling) */\n> +\treturn stride * ((size.height + vertSubSample - 1) / vertSubSample);\n> +}\n> +\n> +/**\n> + * \\brief Compute the number of bytes necessary to store a plane of a frame\n> + * \\param[in] height The height of the frame, in pixels\n> + * \\param[in] plane The plane index\n> + * \\param[in] stride The plane stride, in bytes\n> + *\n> + * The plane size is computed by multiplying the line stride and the frame\n> + * height, taking subsampling and other format characteristics into account.\n> + * Stride alignment constraints may be specified through the \\a align parameter.\n> + *\n> + * \\return The number of bytes necessary to store the plane, or 0 if the\n> + * PixelFormatInfo instance is not valid or the plane number isn't valid for the\n> + * format\n> + */\n> +unsigned int PixelFormatInfo::planeSize(unsigned int height, unsigned int plane,\n> +\t\t\t\t\tunsigned int stride) const\n> +{\n> +\tunsigned int vertSubSample = planes[plane].verticalSubSampling;\n> +\tif (!vertSubSample)\n> +\t\treturn 0;\n> +\n> +\t/* stride * ceil(height / verticalSubSampling) */\n> +\treturn stride * ((height + vertSubSample - 1) / vertSubSample);\n> +}\n> +\n> +/**\n> + * \\brief Compute the number of bytes necessary to store a frame\n> + * \\param[in] size The size of the frame, in pixels\n> + * \\param[in] align The stride alignment, in bytes (1 for default alignment)\n> + *\n> + * The frame size is computed by adding the size of all planes, as computed by\n> + * planeSize(), using the specified alignment constraints for all planes. For\n> + * more complex stride constraints, use the frameSize() overloaded version that\n> + * takes an array of stride values.\n> + *\n> + * \\sa planeSize()\n> + *\n>   * \\return The number of bytes necessary to store the frame, or 0 if the\n>   * PixelFormatInfo instance is not valid\n>   */\n>  unsigned int PixelFormatInfo::frameSize(const Size &size, unsigned int align) const\n>  {\n> -\t/* stride * ceil(height / verticalSubSampling) */\n>  \tunsigned int sum = 0;\n> -\tfor (unsigned int i = 0; i < 3; i++) {\n> -\t\tunsigned int vertSubSample = planes[i].verticalSubSampling;\n> -\t\tif (!vertSubSample)\n> -\t\t\tcontinue;\n> -\t\tsum += stride(size.width, i, align)\n> -\t\t     * ((size.height + vertSubSample - 1) / vertSubSample);\n> +\n> +\tfor (const auto &[i, plane] : utils::enumerate(planes)) {\n> +\t\tif (plane.bytesPerGroup == 0)\n> +\t\t\tbreak;\n> +\n> +\t\tsum += planeSize(size, i, align);\n>  \t}\n>  \n>  \treturn sum;\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 4e1c2b7cef5e..adabd4720668 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -1337,11 +1337,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n>  \t\t\tplanes[i].offset = offset;\n>  \n>  \t\t\t/* \\todo Take the V4L2 stride into account */\n> -\t\t\tconst unsigned int vertSubSample =\n> -\t\t\t\tinfo.planes[i].verticalSubSampling;\n> -\t\t\tplanes[i].length =\n> -\t\t\t\tinfo.stride(format_.size.width, i, 1u) *\n> -\t\t\t\t((format_.size.height + vertSubSample - 1) / vertSubSample);\n> +\t\t\tplanes[i].length = info.planeSize(format_.size, i);\n>  \t\t\toffset += planes[i].length;\n>  \t\t}\n>  \t}\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 20BDEBD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 08:49:58 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4D39269166;\n\tThu,  2 Sep 2021 10:49:57 +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 82BDE60254\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 10:49:55 +0200 (CEST)","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 0EB5B532;\n\tThu,  2 Sep 2021 10:49:55 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"NijeRCcO\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630572595;\n\tbh=lwUP99BsdXisyZhIQYF1HPdwwOd2C2JZPYni1yyepIM=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=NijeRCcO9vZgFnRd3xj9afvM9ia5F+I9LsSHWgNKHtWLvGb+kodo5E9/rLBkT2qqX\n\tiqPF4dzWlT+7cFX7IqSY3L2dfKWKoRe6s9aUAShprarW3cWDP5/mYRwQzSyG/8LJhT\n\tnVLzTj6nGF5XWFiPLxEta7aXJuU6vVqoaCqK0oF4=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-4-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<65e06923-3532-432d-3f13-21044379d83a@ideasonboard.com>","Date":"Thu, 2 Sep 2021 09:49:52 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-4-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 03/12] libcamera: formats: Add\n\tplaneSize() helpers to PixelFormatInfo","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19275,"web_url":"https://patchwork.libcamera.org/comment/19275/","msgid":"<797f3608-e856-3094-5e01-4c71c19cee0e@ideasonboard.com>","date":"2021-09-02T09:23:48","subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:22, Laurent Pinchart wrote:\n> V4L2 describes multi-planar formats with different 4CCs depending on\n> whether or not the planes are stored contiguously in memory. Support\n> this when translating between PixelFormat and V4L2PixelFormat.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/internal/formats.h |   2 +-\n>  src/libcamera/formats.cpp            | 141 ++++++++++++++++-----------\n>  src/libcamera/pipeline/ipu3/cio2.cpp |   2 +-\n>  src/libcamera/v4l2_pixelformat.cpp   |  11 ++-\n>  src/v4l2/v4l2_camera_proxy.cpp       |   6 +-\n>  5 files changed, 99 insertions(+), 63 deletions(-)\n> \n> diff --git a/include/libcamera/internal/formats.h b/include/libcamera/internal/formats.h\n> index 8f314e99889b..f9ce5f0834fa 100644\n> --- a/include/libcamera/internal/formats.h\n> +++ b/include/libcamera/internal/formats.h\n> @@ -55,7 +55,7 @@ public:\n>  \t/* \\todo Add support for non-contiguous memory planes */\n>  \tconst char *name;\n>  \tPixelFormat format;\n> -\tV4L2PixelFormat v4l2Format;\n> +\tstd::array<V4L2PixelFormat, 2> v4l2Format;\n\nAs discussed below, I'm curious if a struct would be clearer here and\nhelp prevent bugs later...\n\n\tstruct {\n\t\tV4L2PixelFormat planar;\n\t\tV4L2PixelFormat multiplanar;\n\t} v4l2Format;\n\n\n>  \tunsigned int bitsPerPixel;\n>  \tenum ColourEncoding colourEncoding;\n>  \tbool packed;\n> diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp\n> index 76be93bc1c5c..ce7c5a2d267e 100644\n> --- a/src/libcamera/formats.cpp\n> +++ b/src/libcamera/formats.cpp\n> @@ -56,7 +56,14 @@ LOG_DEFINE_CATEGORY(Formats)\n>   * \\brief The PixelFormat described by this instance\n>   *\n>   * \\var PixelFormatInfo::v4l2Format\n> - * \\brief The V4L2 pixel format corresponding to the PixelFormat\n> + * \\brief The V4L2 pixel formats corresponding to the PixelFormat\n> + *\n> + * Multiple V4L2 formats may exist for one PixelFormat when the format uses\n> + * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate\n> + * planes formats. The two entries in the array store the contiguous and\n> + * non-contiguous V4L2 formats respectively. If the PixelFormat isn't a\n> + * multiplanar format, or if no corresponding non-contiguous V4L2 format\n> + * exists, the second entry is invalid.\n>   *\n>   * \\var PixelFormatInfo::bitsPerPixel\n>   * \\brief The average number of bits per pixel\n> @@ -149,7 +156,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::RGB565, {\n>  \t\t.name = \"RGB565\",\n>  \t\t.format = formats::RGB565,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -159,7 +166,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::RGB565_BE, {\n>  \t\t.name = \"RGB565_BE\",\n>  \t\t.format = formats::RGB565_BE,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565X),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565X), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -169,7 +176,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::BGR888, {\n>  \t\t.name = \"BGR888\",\n>  \t\t.format = formats::BGR888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB24), },\n>  \t\t.bitsPerPixel = 24,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -179,7 +186,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::RGB888, {\n>  \t\t.name = \"RGB888\",\n>  \t\t.format = formats::RGB888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGR24), },\n>  \t\t.bitsPerPixel = 24,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -189,7 +196,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::XRGB8888, {\n>  \t\t.name = \"XRGB8888\",\n>  \t\t.format = formats::XRGB8888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XBGR32),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XBGR32), },\n>  \t\t.bitsPerPixel = 32,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -199,7 +206,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::XBGR8888, {\n>  \t\t.name = \"XBGR8888\",\n>  \t\t.format = formats::XBGR8888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBX32),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBX32), },\n>  \t\t.bitsPerPixel = 32,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -209,7 +216,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::BGRX8888, {\n>  \t\t.name = \"BGRX8888\",\n>  \t\t.format = formats::BGRX8888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XRGB32),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XRGB32), },\n>  \t\t.bitsPerPixel = 32,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -219,7 +226,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::ABGR8888, {\n>  \t\t.name = \"ABGR8888\",\n>  \t\t.format = formats::ABGR8888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBA32), },\n>  \t\t.bitsPerPixel = 32,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -229,7 +236,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::ARGB8888, {\n>  \t\t.name = \"ARGB8888\",\n>  \t\t.format = formats::ARGB8888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ABGR32), },\n>  \t\t.bitsPerPixel = 32,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -239,7 +246,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::BGRA8888, {\n>  \t\t.name = \"BGRA8888\",\n>  \t\t.format = formats::BGRA8888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ARGB32), },\n>  \t\t.bitsPerPixel = 32,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -249,7 +256,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::RGBA8888, {\n>  \t\t.name = \"RGBA8888\",\n>  \t\t.format = formats::RGBA8888,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGRA32), },\n>  \t\t.bitsPerPixel = 32,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n>  \t\t.packed = false,\n> @@ -261,7 +268,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::YUYV, {\n>  \t\t.name = \"YUYV\",\n>  \t\t.format = formats::YUYV,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YUYV), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -271,7 +278,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::YVYU, {\n>  \t\t.name = \"YVYU\",\n>  \t\t.format = formats::YVYU,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YVYU), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -281,7 +288,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::UYVY, {\n>  \t\t.name = \"UYVY\",\n>  \t\t.format = formats::UYVY,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_UYVY), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -291,7 +298,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::VYUY, {\n>  \t\t.name = \"VYUY\",\n>  \t\t.format = formats::VYUY,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_VYUY), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -303,7 +310,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::NV12, {\n>  \t\t.name = \"NV12\",\n>  \t\t.format = formats::NV12,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> +\t\t.v4l2Format = {\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV12M),\n> +\t\t},\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -313,7 +323,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::NV21, {\n>  \t\t.name = \"NV21\",\n>  \t\t.format = formats::NV21,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> +\t\t.v4l2Format = {\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV21M),\n> +\t\t},\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -323,7 +336,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::NV16, {\n>  \t\t.name = \"NV16\",\n>  \t\t.format = formats::NV16,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> +\t\t.v4l2Format = {\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV16M),\n> +\t\t},\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -333,7 +349,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::NV61, {\n>  \t\t.name = \"NV61\",\n>  \t\t.format = formats::NV61,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> +\t\t.v4l2Format = {\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV61M),\n> +\t\t},\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -343,7 +362,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::NV24, {\n>  \t\t.name = \"NV24\",\n>  \t\t.format = formats::NV24,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV24),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV24), },\n>  \t\t.bitsPerPixel = 24,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -353,7 +372,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::NV42, {\n>  \t\t.name = \"NV42\",\n>  \t\t.format = formats::NV42,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV42),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV42), },\n>  \t\t.bitsPerPixel = 24,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -363,7 +382,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::YUV420, {\n>  \t\t.name = \"YUV420\",\n>  \t\t.format = formats::YUV420,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> +\t\t.v4l2Format = {\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV420M),\n> +\t\t},\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -373,7 +395,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::YVU420, {\n>  \t\t.name = \"YVU420\",\n>  \t\t.format = formats::YVU420,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> +\t\t.v4l2Format = {\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YVU420M),\n> +\t\t},\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -383,7 +408,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::YUV422, {\n>  \t\t.name = \"YUV422\",\n>  \t\t.format = formats::YUV422,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> +\t\t.v4l2Format = {\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV422M),\n> +\t\t},\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -395,7 +423,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::R8, {\n>  \t\t.name = \"R8\",\n>  \t\t.format = formats::R8,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_GREY),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_GREY), },\n>  \t\t.bitsPerPixel = 8,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -407,7 +435,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SBGGR8, {\n>  \t\t.name = \"SBGGR8\",\n>  \t\t.format = formats::SBGGR8,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), },\n>  \t\t.bitsPerPixel = 8,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -417,7 +445,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGBRG8, {\n>  \t\t.name = \"SGBRG8\",\n>  \t\t.format = formats::SGBRG8,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), },\n>  \t\t.bitsPerPixel = 8,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -427,7 +455,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGRBG8, {\n>  \t\t.name = \"SGRBG8\",\n>  \t\t.format = formats::SGRBG8,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), },\n>  \t\t.bitsPerPixel = 8,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -437,7 +465,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SRGGB8, {\n>  \t\t.name = \"SRGGB8\",\n>  \t\t.format = formats::SRGGB8,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), },\n>  \t\t.bitsPerPixel = 8,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -447,7 +475,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SBGGR10, {\n>  \t\t.name = \"SBGGR10\",\n>  \t\t.format = formats::SBGGR10,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -457,7 +485,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGBRG10, {\n>  \t\t.name = \"SGBRG10\",\n>  \t\t.format = formats::SGBRG10,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -467,7 +495,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGRBG10, {\n>  \t\t.name = \"SGRBG10\",\n>  \t\t.format = formats::SGRBG10,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -477,7 +505,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SRGGB10, {\n>  \t\t.name = \"SRGGB10\",\n>  \t\t.format = formats::SRGGB10,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -487,7 +515,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SBGGR10_CSI2P, {\n>  \t\t.name = \"SBGGR10_CSI2P\",\n>  \t\t.format = formats::SBGGR10_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -497,7 +525,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGBRG10_CSI2P, {\n>  \t\t.name = \"SGBRG10_CSI2P\",\n>  \t\t.format = formats::SGBRG10_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -507,7 +535,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGRBG10_CSI2P, {\n>  \t\t.name = \"SGRBG10_CSI2P\",\n>  \t\t.format = formats::SGRBG10_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -517,7 +545,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SRGGB10_CSI2P, {\n>  \t\t.name = \"SRGGB10_CSI2P\",\n>  \t\t.format = formats::SRGGB10_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -527,7 +555,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SBGGR12, {\n>  \t\t.name = \"SBGGR12\",\n>  \t\t.format = formats::SBGGR12,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -537,7 +565,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGBRG12, {\n>  \t\t.name = \"SGBRG12\",\n>  \t\t.format = formats::SGBRG12,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -547,7 +575,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGRBG12, {\n>  \t\t.name = \"SGRBG12\",\n>  \t\t.format = formats::SGRBG12,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -557,7 +585,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SRGGB12, {\n>  \t\t.name = \"SRGGB12\",\n>  \t\t.format = formats::SRGGB12,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -567,7 +595,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SBGGR12_CSI2P, {\n>  \t\t.name = \"SBGGR12_CSI2P\",\n>  \t\t.format = formats::SBGGR12_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -577,7 +605,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGBRG12_CSI2P, {\n>  \t\t.name = \"SGBRG12_CSI2P\",\n>  \t\t.format = formats::SGBRG12_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -587,7 +615,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGRBG12_CSI2P, {\n>  \t\t.name = \"SGRBG12_CSI2P\",\n>  \t\t.format = formats::SGRBG12_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -597,7 +625,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SRGGB12_CSI2P, {\n>  \t\t.name = \"SRGGB12_CSI2P\",\n>  \t\t.format = formats::SRGGB12_CSI2P,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), },\n>  \t\t.bitsPerPixel = 12,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -607,7 +635,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SBGGR16, {\n>  \t\t.name = \"SBGGR16\",\n>  \t\t.format = formats::SBGGR16,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -617,7 +645,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGBRG16, {\n>  \t\t.name = \"SGBRG16\",\n>  \t\t.format = formats::SGBRG16,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -627,7 +655,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGRBG16, {\n>  \t\t.name = \"SGRBG16\",\n>  \t\t.format = formats::SGRBG16,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -637,7 +665,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SRGGB16, {\n>  \t\t.name = \"SRGGB16\",\n>  \t\t.format = formats::SRGGB16,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), },\n>  \t\t.bitsPerPixel = 16,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = false,\n> @@ -647,7 +675,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SBGGR10_IPU3, {\n>  \t\t.name = \"SBGGR10_IPU3\",\n>  \t\t.format = formats::SBGGR10_IPU3,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -658,7 +686,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGBRG10_IPU3, {\n>  \t\t.name = \"SGBRG10_IPU3\",\n>  \t\t.format = formats::SGBRG10_IPU3,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -668,7 +696,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SGRBG10_IPU3, {\n>  \t\t.name = \"SGRBG10_IPU3\",\n>  \t\t.format = formats::SGRBG10_IPU3,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -678,7 +706,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::SRGGB10_IPU3, {\n>  \t\t.name = \"SRGGB10_IPU3\",\n>  \t\t.format = formats::SRGGB10_IPU3,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10), },\n>  \t\t.bitsPerPixel = 10,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n>  \t\t.packed = true,\n> @@ -690,7 +718,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n>  \t{ formats::MJPEG, {\n>  \t\t.name = \"MJPEG\",\n>  \t\t.format = formats::MJPEG,\n> -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),\n> +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_MJPEG), },\n>  \t\t.bitsPerPixel = 0,\n>  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n>  \t\t.packed = false,\n> @@ -736,7 +764,8 @@ const PixelFormatInfo &PixelFormatInfo::info(const V4L2PixelFormat &format)\n>  {\n>  \tconst auto &info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),\n>  \t\t\t\t\t[format](auto pair) {\n> -\t\t\t\t\t\treturn pair.second.v4l2Format == format;\n> +\t\t\t\t\t\treturn pair.second.v4l2Format[0] == format ||\n> +\t\t\t\t\t\t       pair.second.v4l2Format[1] == format;\n>  \t\t\t\t\t});\n>  \tif (info == pixelFormatInfo.end())\n>  \t\treturn pixelFormatInfoInvalid;\n> diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp\n> index 9cedcb5b2879..5a9cffc80c8d 100644\n> --- a/src/libcamera/pipeline/ipu3/cio2.cpp\n> +++ b/src/libcamera/pipeline/ipu3/cio2.cpp\n> @@ -205,7 +205,7 @@ int CIO2Device::configure(const Size &size, V4L2DeviceFormat *outputFormat)\n>  \n>  \tconst PixelFormatInfo &info = PixelFormatInfo::info(itInfo->second);\n>  \n> -\toutputFormat->fourcc = info.v4l2Format;\n> +\toutputFormat->fourcc = info.v4l2Format[0];\n>  \toutputFormat->size = sensorFormat.size;\n>  \toutputFormat->planesCount = 1;\n>  \n> diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp\n> index 93fc4446cc64..93ead8928ed7 100644\n> --- a/src/libcamera/v4l2_pixelformat.cpp\n> +++ b/src/libcamera/v4l2_pixelformat.cpp\n> @@ -67,12 +67,19 @@ const std::map<V4L2PixelFormat, PixelFormat> vpf2pf{\n>  \n>  \t/* YUV planar formats. */\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV16), formats::NV16 },\n> +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV16M), formats::NV16 },\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV61), formats::NV61 },\n> +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV61M), formats::NV61 },\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV12), formats::NV12 },\n> +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV12M), formats::NV12 },\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV21), formats::NV21 },\n> +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV21M), formats::NV21 },\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV420), formats::YUV420 },\n> +\t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV420M), formats::YUV420 },\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_YVU420), formats::YVU420 },\n> +\t{ V4L2PixelFormat(V4L2_PIX_FMT_YVU420M), formats::YVU420 },\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV422P), formats::YUV422 },\n> +\t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV422M), formats::YUV422 },\n\nI'm worried that we're losing information here.\nBut presumably it's handled by whether we use mplane or not on the\ndevice...?\n\n\n>  \n>  \t/* Greyscale formats. */\n>  \t{ V4L2PixelFormat(V4L2_PIX_FMT_GREY), formats::R8 },\n> @@ -202,13 +209,13 @@ PixelFormat V4L2PixelFormat::toPixelFormat() const\n>   * \\return The V4L2PixelFormat corresponding to \\a pixelFormat\n>   */\n>  V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat,\n> -\t\t\t\t\t\t [[maybe_unused]] bool multiplanar)\n> +\t\t\t\t\t\t bool multiplanar)\n>  {\n>  \tconst PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);\n>  \tif (!info.isValid())\n>  \t\treturn V4L2PixelFormat();\n>  \n> -\treturn info.v4l2Format;\n> +\treturn info.v4l2Format[multiplanar ? 1 : 0];\n\nI was going to say, a struct with named fields of 'planar', and\n'multiplanar' might be a better, though I like the simplicity of this bit.\n\n\n\n>  }\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> index 7682c4bddf90..8d8ee395954f 100644\n> --- a/src/v4l2/v4l2_camera_proxy.cpp\n> +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> @@ -169,7 +169,7 @@ void V4L2CameraProxy::setFmtFromConfig(const StreamConfiguration &streamConfig)\n>  \n>  \tv4l2PixFormat_.width        = size.width;\n>  \tv4l2PixFormat_.height       = size.height;\n> -\tv4l2PixFormat_.pixelformat  = info.v4l2Format;\n> +\tv4l2PixFormat_.pixelformat  = info.v4l2Format[0];\n>  \tv4l2PixFormat_.field        = V4L2_FIELD_NONE;\n>  \tv4l2PixFormat_.bytesperline = streamConfig.stride;\n>  \tv4l2PixFormat_.sizeimage    = streamConfig.frameSize;\n> @@ -276,7 +276,7 @@ int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *file, struct v4l2_fmtdesc *\n>  \t/* \\todo Add map from format to description. */\n>  \tutils::strlcpy(reinterpret_cast<char *>(arg->description),\n>  \t\t       \"Video Format Description\", sizeof(arg->description));\n> -\targ->pixelformat = PixelFormatInfo::info(format).v4l2Format;\n> +\targ->pixelformat = PixelFormatInfo::info(format).v4l2Format[0];\n>  \n>  \tmemset(arg->reserved, 0, sizeof(arg->reserved));\n>  \n> @@ -315,7 +315,7 @@ int V4L2CameraProxy::tryFormat(struct v4l2_format *arg)\n>  \n>  \targ->fmt.pix.width        = config.size.width;\n>  \targ->fmt.pix.height       = config.size.height;\n> -\targ->fmt.pix.pixelformat  = info.v4l2Format;\n> +\targ->fmt.pix.pixelformat  = info.v4l2Format[0];\n\nBut for occasions like here, and in the CIO2\n\targ->fmt.pix.pixelformat  = info.v4l2Format.planar;\n\nWould be far more descriptive over which one is being chosen, and might\nhelp make it easier to spot issues when debugging multiplanar format\nbugs ...\n\n>  \targ->fmt.pix.field        = V4L2_FIELD_NONE;\n>  \targ->fmt.pix.bytesperline = config.stride;\n>  \targ->fmt.pix.sizeimage    = config.frameSize;\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 DDA27BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 09:23:53 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 32F436916E;\n\tThu,  2 Sep 2021 11:23:53 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3FFE160254\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 11:23:52 +0200 (CEST)","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 BDADD45E;\n\tThu,  2 Sep 2021 11:23:51 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"q7MH7NM4\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630574631;\n\tbh=Uni5K84KGFzcpSh1lZ4SjMvX2vXtMKUjX/gKm111oMk=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=q7MH7NM4/kLFLTw+MiVo4AFjbQVkdkwAuQqIy5iZVKHnRvJ5gyA6mhs4vD+oYm5hA\n\tMfb8HAnWUoI3ARj2cVnd3ZBqdlYTcX5g6Xi9Egqp5M1av2RB5bMHp62ra9sBXfQNww\n\tGyl1xgpJJ1nnFF8KR55amouobRi77M44W57kbc9o=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-5-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<797f3608-e856-3094-5e01-4c71c19cee0e@ideasonboard.com>","Date":"Thu, 2 Sep 2021 10:23:48 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-5-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19277,"web_url":"https://patchwork.libcamera.org/comment/19277/","msgid":"<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>","date":"2021-09-02T09:43:56","subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:22, Laurent Pinchart wrote:\n> Multi-planar frame buffers can store their planes contiguously in\n> memory, or split them in discontiguous memory areas. Add a private\n> function to check in which of these two categories the frame buffer\n> belongs. This will be used to correctly handle the differences between\n> the V4L2 single and multi planar APIs.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/internal/framebuffer.h |  2 ++\n>  src/libcamera/framebuffer.cpp            | 45 +++++++++++++++++++++++-\n>  2 files changed, 46 insertions(+), 1 deletion(-)\n> \n> diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h\n> index 606aed2b4782..cd33c295466e 100644\n> --- a/include/libcamera/internal/framebuffer.h\n> +++ b/include/libcamera/internal/framebuffer.h\n> @@ -21,9 +21,11 @@ public:\n>  \tPrivate();\n>  \n>  \tvoid setRequest(Request *request) { request_ = request; }\n> +\tbool isContiguous() const { return isContiguous_; }\n>  \n>  private:\n>  \tRequest *request_;\n> +\tbool isContiguous_;\n>  };\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> index 53ef89bf458f..99265b44da43 100644\n> --- a/src/libcamera/framebuffer.cpp\n> +++ b/src/libcamera/framebuffer.cpp\n> @@ -106,7 +106,7 @@ LOG_DEFINE_CATEGORY(Buffer)\n>   */\n>  \n>  FrameBuffer::Private::Private()\n> -\t: request_(nullptr)\n> +\t: request_(nullptr), isContiguous_(true)\n>  {\n>  }\n>  \n> @@ -120,6 +120,17 @@ FrameBuffer::Private::Private()\n>   * handlers, it is called by the pipeline handlers themselves.\n>   */\n>  \n> +/**\n> + * \\fn FrameBuffer::Private::isContiguous()\n> + * \\brief Check if the frame buffer stores planes contiguously in memory\n> + *\n> + * Multi-planar frame buffers can store their planes contiguously in memory, or\n> + * split them in discontiguous memory areas. This function checks in which of\n\n'split them into discontiguous'\n\n> + * these two categories the frame buffer belongs.\n> + *\n> + * \\return True if the planes are stored contiguously in memory, false otherwise\n> + */\n> +\n>  /**\n>   * \\class FrameBuffer\n>   * \\brief Frame buffer data and its associated dynamic metadata\n> @@ -199,9 +210,41 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n>  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n>  \t  cookie_(cookie)\n>  {\n> +\tunsigned int offset = 0;\n> +\n>  \t/* \\todo Remove the assertions after sufficient testing */\n>  \tfor (const auto &plane : planes_)\n>  \t\tASSERT(plane.offset != Plane::kInvalidOffset);\n> +\n> +\tbool isContiguous = true;\n> +\tino_t inode = 0;\n> +\n> +\tfor (const auto &plane : planes_) {\n> +\t\tif (plane.offset != offset) {\n> +\t\t\tisContiguous = false;\n> +\t\t\tbreak;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Two different dmabuf file descriptors may still refer to the\n> +\t\t * same dmabuf instance. Check this using inodes.\n\nGoing back to the FileDescriptor::inode() extension, if that cached the\ninode (it can't change) on either first call, or construction, couldn't\nthis whole check be simplified to:\n\n\t\tif (plane.fd.inode() != planes_[0].fd.inode()) {\n\t\t\tisContiguous = false;\n\t\t\tbreak;\n\t\t}\n\n> +\t\t */\n> +\t\tif (plane.fd.fd() != planes_[0].fd.fd()) {\n> +\t\t\tif (!inode)\n> +\t\t\t\tinode = planes_[0].fd.inode();\n> +\t\t\tif (plane.fd.inode() != inode) {\n> +\t\t\t\tisContiguous = false;\n> +\t\t\t\tbreak;\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\toffset += plane.length;\n> +\t}\n> +\n> +\tLOG(Buffer, Debug)\n> +\t\t<< \"Buffer is \" << (isContiguous ? \"not \" : \"\") << \"contiguous\";\n> +\n> +\t_d()->isContiguous_ = isContiguous;\n>  }\n>  \n>  /**\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 728D5BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 09:44:01 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C06986916A;\n\tThu,  2 Sep 2021 11:44:00 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4A32C60254\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 11:43:59 +0200 (CEST)","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 C2DA045E;\n\tThu,  2 Sep 2021 11:43:58 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"qtvUwyZ+\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630575838;\n\tbh=xPMvPemTZ+K58uMPNf/J6x57uKb8inL1hbL/NrW5gps=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=qtvUwyZ+G59Ijy9YB6DonxM38X4TZDILUcs8wNSz3hP9bgVJP6tW4TgjXvEKDW08H\n\tv2F/hNqvqDZ1AIBND1KIatfpYq+IBCZtp6m2XNK6YesKV6pMBH4ZTmK8QmDV8iPcTE\n\t5ozCxuwZgX40mpqiHhqgj1XKK9Cz7G/PKWoKgCFY=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>","Date":"Thu, 2 Sep 2021 10:43:56 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19278,"web_url":"https://patchwork.libcamera.org/comment/19278/","msgid":"<a36b82c0-e1fe-9cba-0eb7-814391fc68fe@ideasonboard.com>","date":"2021-09-02T09:54:19","subject":"Re: [libcamera-devel] [RFC PATCH v1 07/12] libcamera: framebuffer:\n\tAllocate metadata planes at construction time","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:22, Laurent Pinchart wrote:\n> The metadata planes are allocated by V4L2VideoDevice when dequeuing a\n> buffer. This causes the metadata planes to only be allocated after a\n> buffer gets dequeued, and doesn't provide any strong guarantee that\n> their number matches the number of FrameBuffer planes. The lack of this\n> invariant makes the FrameBuffer class fragile.\n> \n> As a first step towards fixing this, allocate the metadata planes when\n> the FrameBuffer is constructed. The FrameMetadata API should be further\n> improved by preventing a change in the number of planes.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  src/libcamera/framebuffer.cpp      |  2 ++\n>  src/libcamera/v4l2_videodevice.cpp | 12 +++++++++---\n>  2 files changed, 11 insertions(+), 3 deletions(-)\n> \n> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> index 99265b44da43..c1529dfb0ad1 100644\n> --- a/src/libcamera/framebuffer.cpp\n> +++ b/src/libcamera/framebuffer.cpp\n> @@ -210,6 +210,8 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n>  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n>  \t  cookie_(cookie)\n>  {\n> +\tmetadata_.planes.resize(planes_.size());\n> +\n>  \tunsigned int offset = 0;\n>  \n>  \t/* \\todo Remove the assertions after sufficient testing */\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index adabd4720668..a51971879e75 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -1586,12 +1586,18 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n>  \tbuffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL\n>  \t\t\t\t    + buf.timestamp.tv_usec * 1000ULL;\n>  \n> -\tbuffer->metadata_.planes.clear();\n>  \tif (multiPlanar) {\n> +\t\tif (buf.length > buffer->metadata_.planes.size()) {\n> +\t\t\tLOG(V4L2, Error)\n\nAre we going to 'leak' queued FrameBuffers at this point?\nIs there a way to prevent this occurring at queue time rather than dequeue?\n\nIn fact, Can it even happen? or should it be an ASSERT/Fatal?\n\n\n> +\t\t\t\t<< \"Invalid number of planes (\" << buf.length\n> +\t\t\t\t<< \" != \" << buffer->metadata_.planes.size() << \")\";\n> +\t\t\treturn nullptr;\n> +\t\t}\n> +\n>  \t\tfor (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> -\t\t\tbuffer->metadata_.planes.push_back({ planes[nplane].bytesused });\n> +\t\t\tbuffer->metadata_.planes[nplane].bytesused = planes[nplane].bytesused;\n>  \t} else {\n> -\t\tbuffer->metadata_.planes.push_back({ buf.bytesused });\n> +\t\tbuffer->metadata_.planes[0].bytesused = buf.bytesused;\n>  \t}\n>  \n>  \treturn buffer;\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 88170BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 09:54:24 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D197D69166;\n\tThu,  2 Sep 2021 11:54:23 +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 5A25B60254\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 11:54:22 +0200 (CEST)","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 DAC1845E;\n\tThu,  2 Sep 2021 11:54:21 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Hji7LRVI\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630576462;\n\tbh=zRRi/HccwVzzwAIuxtvYQrYZu25VvZIM6votBKUtoaM=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=Hji7LRVIOoYAl+BRmh7irVgkdsYUofVoGiZirfVftT5nlqyLWJ3Aij3L67e4GK9Fi\n\tQH77RvrV2QOLVznOzy+Gqtt4eA7Csl5wklQYakeFTVP5JOQ2agoILFkT/E+Xm0A1Sz\n\tLFQqp5iznDEUZwJxiJD2Cixnb9inyGnPSyHujb5s=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-8-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<a36b82c0-e1fe-9cba-0eb7-814391fc68fe@ideasonboard.com>","Date":"Thu, 2 Sep 2021 10:54:19 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-8-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 07/12] libcamera: framebuffer:\n\tAllocate metadata planes at construction time","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19280,"web_url":"https://patchwork.libcamera.org/comment/19280/","msgid":"<23fb7400-0ebd-276b-52f9-863442ebf4b1@ideasonboard.com>","date":"2021-09-02T10:03:47","subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:22, Laurent Pinchart wrote:\n> The number of metadata planes should always match the number of frame\n> buffer planes. Enforce this by making the vector private and providing\n> accessor functions.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/framebuffer.h    | 10 +++++++++-\n>  src/cam/camera_session.cpp         |  4 ++--\n>  src/cam/file_sink.cpp              |  2 +-\n>  src/libcamera/framebuffer.cpp      | 12 +++++++++---\n>  src/libcamera/v4l2_videodevice.cpp | 14 +++++++-------\n>  src/qcam/main_window.cpp           |  2 +-\n>  src/qcam/viewfinder_gl.cpp         |  2 +-\n>  src/qcam/viewfinder_qt.cpp         |  2 +-\n>  src/v4l2/v4l2_camera_proxy.cpp     |  2 +-\n>  9 files changed, 32 insertions(+), 18 deletions(-)\n> \n> diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n> index fd68ed0a139d..7f2f176af691 100644\n> --- a/include/libcamera/framebuffer.h\n> +++ b/include/libcamera/framebuffer.h\n> @@ -13,6 +13,7 @@\n>  #include <vector>\n>  \n>  #include <libcamera/base/class.h>\n> +#include <libcamera/base/span.h>\n>  \n>  #include <libcamera/file_descriptor.h>\n>  \n> @@ -34,7 +35,14 @@ struct FrameMetadata {\n>  \tStatus status;\n>  \tunsigned int sequence;\n>  \tuint64_t timestamp;\n> -\tstd::vector<Plane> planes;\n> +\n> +\tSpan<Plane> planes() { return planes_; }\n> +\tSpan<const Plane> planes() const { return planes_; }\n\nReturning a span here is nice.\n\nThis likely causes compile breakage for any external app.\n\nI know we're not ABI/API stable, but I wonder if we should highlight\nwhen we do cause breakage somehow, perhaps in the commit message at\nleast, as we already have external users who we might want to notify.\n\n\n> +\n> +private:\n> +\tfriend class FrameBuffer;\n> +\n> +\tstd::vector<Plane> planes_;\n>  };\n>  \n>  class FrameBuffer final : public Extensible\n> diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\n> index 60d640f2b15c..32a373a99b72 100644\n> --- a/src/cam/camera_session.cpp\n> +++ b/src/cam/camera_session.cpp\n> @@ -374,9 +374,9 @@ void CameraSession::processRequest(Request *request)\n>  \t\t     << \" bytesused: \";\n>  \n>  \t\tunsigned int nplane = 0;\n> -\t\tfor (const FrameMetadata::Plane &plane : metadata.planes) {\n> +\t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n>  \t\t\tinfo << plane.bytesused;\n> -\t\t\tif (++nplane < metadata.planes.size())\n> +\t\t\tif (++nplane < metadata.planes().size())\n>  \t\t\t\tinfo << \"/\";\n>  \t\t}\n>  \t}\n> diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\n> index 0b529e3eb767..0fc7d621f50b 100644\n> --- a/src/cam/file_sink.cpp\n> +++ b/src/cam/file_sink.cpp\n> @@ -110,7 +110,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)\n>  \n>  \tfor (unsigned int i = 0; i < buffer->planes().size(); ++i) {\n>  \t\tconst FrameBuffer::Plane &plane = buffer->planes()[i];\n> -\t\tconst FrameMetadata::Plane &meta = buffer->metadata().planes[i];\n> +\t\tconst FrameMetadata::Plane &meta = buffer->metadata().planes()[i];\n>  \n>  \t\tuint8_t *data = planeData_[&plane];\n>  \t\tunsigned int length = std::min(meta.bytesused, plane.length);\n> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> index c1529dfb0ad1..83cf7b83d182 100644\n> --- a/src/libcamera/framebuffer.cpp\n> +++ b/src/libcamera/framebuffer.cpp\n> @@ -91,8 +91,14 @@ LOG_DEFINE_CATEGORY(Buffer)\n>   */\n>  \n>  /**\n> - * \\var FrameMetadata::planes\n> - * \\brief Array of per-plane metadata\n> + * \\fn FrameMetadata::planes()\n> + * \\copydoc FrameMetadata::planes() const\n> + */\n> +\n> +/**\n> + * \\fn FrameMetadata::planes() const\n> + * \\brief Retrieve the array of per-plane metadata\n> + * \\return The array of per-plane metadata\n>   */\n>  \n>  /**\n> @@ -210,7 +216,7 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n>  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n>  \t  cookie_(cookie)\n>  {\n> -\tmetadata_.planes.resize(planes_.size());\n> +\tmetadata_.planes_.resize(planes_.size());\n>  \n>  \tunsigned int offset = 0;\n>  \n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index a51971879e75..82ddaed3656f 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -1485,14 +1485,14 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n>  \n>  \t\tif (multiPlanar) {\n>  \t\t\tunsigned int nplane = 0;\n> -\t\t\tfor (const FrameMetadata::Plane &plane : metadata.planes) {\n> +\t\t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n>  \t\t\t\tv4l2Planes[nplane].bytesused = plane.bytesused;\n>  \t\t\t\tv4l2Planes[nplane].length = buffer->planes()[nplane].length;\n>  \t\t\t\tnplane++;\n>  \t\t\t}\n>  \t\t} else {\n> -\t\t\tif (metadata.planes.size())\n> -\t\t\t\tbuf.bytesused = metadata.planes[0].bytesused;\n> +\t\t\tif (metadata.planes().size())\n> +\t\t\t\tbuf.bytesused = metadata.planes()[0].bytesused;\n>  \t\t}\n>  \n>  \t\tbuf.sequence = metadata.sequence;\n> @@ -1587,17 +1587,17 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n>  \t\t\t\t    + buf.timestamp.tv_usec * 1000ULL;\n>  \n>  \tif (multiPlanar) {\n> -\t\tif (buf.length > buffer->metadata_.planes.size()) {\n> +\t\tif (buf.length > buffer->metadata_.planes().size()) {\n>  \t\t\tLOG(V4L2, Error)\n>  \t\t\t\t<< \"Invalid number of planes (\" << buf.length\n> -\t\t\t\t<< \" != \" << buffer->metadata_.planes.size() << \")\";\n> +\t\t\t\t<< \" != \" << buffer->metadata_.planes().size() << \")\";\n>  \t\t\treturn nullptr;\n>  \t\t}\n>  \n>  \t\tfor (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> -\t\t\tbuffer->metadata_.planes[nplane].bytesused = planes[nplane].bytesused;\n> +\t\t\tbuffer->metadata_.planes()[nplane].bytesused = planes[nplane].bytesused;\n>  \t} else {\n> -\t\tbuffer->metadata_.planes[0].bytesused = buf.bytesused;\n> +\t\tbuffer->metadata_.planes()[0].bytesused = buf.bytesused;\n>  \t}\n>  \n>  \treturn buffer;\n> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> index 1536b2b5bd66..ac853e360aea 100644\n> --- a/src/qcam/main_window.cpp\n> +++ b/src/qcam/main_window.cpp\n> @@ -756,7 +756,7 @@ void MainWindow::processViewfinder(FrameBuffer *buffer)\n>  \n>  \tqDebug().noquote()\n>  \t\t<< QString(\"seq: %1\").arg(metadata.sequence, 6, 10, QLatin1Char('0'))\n> -\t\t<< \"bytesused:\" << metadata.planes[0].bytesused\n> +\t\t<< \"bytesused:\" << metadata.planes()[0].bytesused\n>  \t\t<< \"timestamp:\" << metadata.timestamp\n>  \t\t<< \"fps:\" << Qt::fixed << qSetRealNumberPrecision(2) << fps;\n>  \n> diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n> index 40226601f9fd..d2ef036974f4 100644\n> --- a/src/qcam/viewfinder_gl.cpp\n> +++ b/src/qcam/viewfinder_gl.cpp\n> @@ -125,7 +125,7 @@ void ViewFinderGL::render(libcamera::FrameBuffer *buffer,\n>  \t/*\n>  \t * \\todo Get the stride from the buffer instead of computing it naively\n>  \t */\n> -\tstride_ = buffer->metadata().planes[0].bytesused / size_.height();\n> +\tstride_ = buffer->metadata().planes()[0].bytesused / size_.height();\n\nCan this be obtained from the PixelFormatInfo now ?\nor do we still not expose that to applications...\n\nAnyway, that's likely a change on top even if we could to solve the \\todo.\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n\n>  \tupdate();\n>  \tbuffer_ = buffer;\n>  }\n> diff --git a/src/qcam/viewfinder_qt.cpp b/src/qcam/viewfinder_qt.cpp\n> index efa1d412584b..a0bf99b0b522 100644\n> --- a/src/qcam/viewfinder_qt.cpp\n> +++ b/src/qcam/viewfinder_qt.cpp\n> @@ -87,7 +87,7 @@ void ViewFinderQt::render(libcamera::FrameBuffer *buffer,\n>  \t}\n>  \n>  \tunsigned char *memory = mem.data();\n> -\tsize_t size = buffer->metadata().planes[0].bytesused;\n> +\tsize_t size = buffer->metadata().planes()[0].bytesused;\n>  \n>  \t{\n>  \t\tQMutexLocker locker(&mutex_);\n> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> index 8d8ee395954f..68e47ee81834 100644\n> --- a/src/v4l2/v4l2_camera_proxy.cpp\n> +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> @@ -212,7 +212,7 @@ void V4L2CameraProxy::updateBuffers()\n>  \n>  \t\tswitch (fmd.status) {\n>  \t\tcase FrameMetadata::FrameSuccess:\n> -\t\t\tbuf.bytesused = fmd.planes[0].bytesused;\n> +\t\t\tbuf.bytesused = fmd.planes()[0].bytesused;\n>  \t\t\tbuf.field = V4L2_FIELD_NONE;\n>  \t\t\tbuf.timestamp.tv_sec = fmd.timestamp / 1000000000;\n>  \t\t\tbuf.timestamp.tv_usec = fmd.timestamp % 1000000;\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 E72F0BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 10:03:52 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6A93F60255;\n\tThu,  2 Sep 2021 12:03:52 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D19D460255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 12:03:50 +0200 (CEST)","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 7BE9445E;\n\tThu,  2 Sep 2021 12:03:50 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"joblR43N\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630577030;\n\tbh=0QJzGck7sU3zYpaBKIY+A8t6whDFTQunqNoqutb5ey8=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=joblR43NKQVZURBj5Ae00JF6SgPmRjQ30qK2I7BuzYSbYX1H9SsQWZKljw1shGqDP\n\t3bSu5IClX89wMk41b0uHIqqGF6MrzO91H8GgK/uxhoaMvEqmTkUlRg/N0GPWk2vCKX\n\t68svTZJIXxvUASWHwpde0K3gRwn9r3E1ogY86uPo=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-9-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<23fb7400-0ebd-276b-52f9-863442ebf4b1@ideasonboard.com>","Date":"Thu, 2 Sep 2021 11:03:47 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-9-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19281,"web_url":"https://patchwork.libcamera.org/comment/19281/","msgid":"<ef9325cc-41f1-efa3-109d-0e5dd238317a@ideasonboard.com>","date":"2021-09-02T10:21:29","subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:23, Laurent Pinchart wrote:\n> Cache the PixelFormatInfo instead of looking it up in every call to\n> createBuffer(). This prepare for usage of the info in queueBuffer(), to\n\ns/prepare/prepares/\n\n> avoid a looking every time a buffer is queued.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/internal/v4l2_videodevice.h |  1 +\n>  src/libcamera/v4l2_videodevice.cpp            | 19 +++++++++++--------\n>  2 files changed, 12 insertions(+), 8 deletions(-)\n> \n> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h\n> index 7a145f608a5b..6096cd609b97 100644\n> --- a/include/libcamera/internal/v4l2_videodevice.h\n> +++ b/include/libcamera/internal/v4l2_videodevice.h\n> @@ -243,6 +243,7 @@ private:\n>  \n>  \tV4L2Capability caps_;\n>  \tV4L2DeviceFormat format_;\n> +\tconst PixelFormatInfo *formatInfo_;\n>  \n>  \tenum v4l2_buf_type bufferType_;\n>  \tenum v4l2_memory memoryType_;\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 82ddaed3656f..2d9a94c3c974 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -482,8 +482,8 @@ const std::string V4L2DeviceFormat::toString() const\n>   * \\param[in] deviceNode The file-system path to the video device node\n>   */\n>  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> -\t: V4L2Device(deviceNode), cache_(nullptr), fdBufferNotifier_(nullptr),\n> -\t  streaming_(false)\n> +\t: V4L2Device(deviceNode), formatInfo_(nullptr), cache_(nullptr),\n> +\t  fdBufferNotifier_(nullptr), streaming_(false)\n>  {\n>  \t/*\n>  \t * We default to an MMAP based CAPTURE video device, however this will\n> @@ -586,6 +586,7 @@ int V4L2VideoDevice::open()\n>  \t\treturn ret;\n>  \t}\n>  \n> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n\nI shudder a little seeing code hugging the return like that ;-)\n\nBut it's purely subjective, same below of course.\n\n\n>  \treturn 0;\n>  }\n>  \n> @@ -681,6 +682,7 @@ int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)\n>  \t\treturn ret;\n>  \t}\n>  \n> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n>  \treturn 0;\n>  }\n>  \n> @@ -695,6 +697,8 @@ void V4L2VideoDevice::close()\n>  \treleaseBuffers();\n>  \tdelete fdBufferNotifier_;\n>  \n> +\tformatInfo_ = nullptr;\n> +\n>  \tV4L2Device::close();\n>  }\n>  \n> @@ -787,6 +791,7 @@ int V4L2VideoDevice::setFormat(V4L2DeviceFormat *format)\n>  \t\treturn ret;\n>  \n>  \tformat_ = *format;\n> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n\nLooks like we were already using that pattern though ;-)\n\n\n>  \treturn 0;\n>  }\n>  \n> @@ -1325,19 +1330,17 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n>  \t\tplanes.push_back(std::move(plane));\n>  \t}\n>  \n> -\tconst auto &info = PixelFormatInfo::info(format_.fourcc);\n> -\tif (info.isValid() && info.numPlanes() != numPlanes) {\n> +\tif (formatInfo_->isValid() && formatInfo_->numPlanes() != numPlanes) {\n>  \t\tASSERT(numPlanes == 1u);\n\nNot for this patch, but I guess this assert needed a\n  \\todo Multiplanar support\n\naround it?\n\nHowever, this patch is just for caching the formatInfo, which seems\nreasonable so\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> -\t\tconst size_t numColorPlanes = info.numPlanes();\n> -\t\tplanes.resize(numColorPlanes);\n> +\t\tplanes.resize(formatInfo_->numPlanes());\n>  \t\tconst FileDescriptor &fd = planes[0].fd;\n>  \t\tsize_t offset = 0;\n> -\t\tfor (size_t i = 0; i < numColorPlanes; ++i) {\n> +\t\tfor (size_t i = 0; i < planes.size(); ++i) {\n>  \t\t\tplanes[i].fd = fd;\n>  \t\t\tplanes[i].offset = offset;\n>  \n>  \t\t\t/* \\todo Take the V4L2 stride into account */\n> -\t\t\tplanes[i].length = info.planeSize(format_.size, i);\n> +\t\t\tplanes[i].length = formatInfo_->planeSize(format_.size, i);\n>  \t\t\toffset += planes[i].length;\n>  \t\t}\n>  \t}\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 916E2BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 10:21:33 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0148B6916A;\n\tThu,  2 Sep 2021 12:21:32 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1EB9A60255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 12:21:32 +0200 (CEST)","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 9ACF945E;\n\tThu,  2 Sep 2021 12:21:31 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"P13/i58h\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630578091;\n\tbh=DFUxnSCSTa/urMFpoEKZz1xCAHbIvcmAgToYGhz/QRI=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=P13/i58hK6dLQYR2dy7lDHewOcB/AxMZR79TaLBcmS2UqvI9C9l2HM/7UL4mQ61/s\n\t57S2QV5dSHw1cr9dXA2UM/QiVTZY36H27dPj34i9un19DieDhstpMu4WxdxaWQpls9\n\t9mKD1HWl8GlR3BrAOyDrXHIx9BdQA1yWVmdTvb6Y=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-10-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<ef9325cc-41f1-efa3-109d-0e5dd238317a@ideasonboard.com>","Date":"Thu, 2 Sep 2021 11:21:29 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-10-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19283,"web_url":"https://patchwork.libcamera.org/comment/19283/","msgid":"<aad3bac8-de8a-f9a0-f590-0f9c26571dfb@ideasonboard.com>","date":"2021-09-02T11:17:31","subject":"Re: [libcamera-devel] [RFC PATCH v1 11/12] libcamera:\n\tv4l2_videodevice: Split planes when dequeuing buffer","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:23, Laurent Pinchart wrote:\n> When dequeueing a buffer from a V4L2VideoDevice, the number of planes in\n> the FrameBuffer may not match the number of V4L2 buffer planes if the\n> PixelFormat is multi-planar (has multiple colour planes) and the V4L2\n> format is single-planar (has a single buffer plane). In this case, we\n> need to split the single V4L2 buffer plane into FrameBuffer planes. Do\n> so, and add checks to reject invalid V4L2 buffers in case of a driver\n> issue.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  src/libcamera/v4l2_videodevice.cpp | 54 ++++++++++++++++++++++++------\n>  1 file changed, 43 insertions(+), 11 deletions(-)\n> \n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 79cb792117d5..b80b9038a914 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -7,6 +7,7 @@\n>  \n>  #include \"libcamera/internal/v4l2_videodevice.h\"\n>  \n> +#include <algorithm>\n>  #include <array>\n>  #include <fcntl.h>\n>  #include <iomanip>\n> @@ -1637,18 +1638,49 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n>  \tbuffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL\n>  \t\t\t\t    + buf.timestamp.tv_usec * 1000ULL;\n>  \n> -\tif (multiPlanar) {\n> -\t\tif (buf.length > buffer->metadata_.planes().size()) {\n> -\t\t\tLOG(V4L2, Error)\n> -\t\t\t\t<< \"Invalid number of planes (\" << buf.length\n> -\t\t\t\t<< \" != \" << buffer->metadata_.planes().size() << \")\";\n> -\t\t\treturn nullptr;\n> +\tif (V4L2_TYPE_IS_CAPTURE(buf.type)) {\n\nShould we swap this around and return the buffer early to lower the\nindents below?\n\n if (!V4L2_TYPE_IS_CAPTURE(buf.type)) {\n\t/* Output buffer is completed */\n\treturn buffer;\n }\n\n\n> +\t\tunsigned int numV4l2Planes = multiPlanar ? buf.length : 1;\n> +\t\tFrameMetadata &metadata = buffer->metadata_;\n> +\n> +\t\tif (numV4l2Planes != metadata.planes().size()) {\n> +\t\t\t/*\n> +\t\t\t * If we have a multi-planar buffer with a V4L2\n> +\t\t\t * single-planar format, split the V4L2 buffer across\n> +\t\t\t * the buffer planes. Only the last plane may have less\n> +\t\t\t * bytes used than its length.\n> +\t\t\t */\n> +\t\t\tif (numV4l2Planes != 1) {\n> +\t\t\t\tLOG(V4L2, Error)\n> +\t\t\t\t\t<< \"Invalid number of planes (\" << numV4l2Planes\n> +\t\t\t\t\t<< \" != \" << metadata.planes().size() << \")\";\n> +\t\t\t\treturn nullptr;\n> +\t\t\t}\n> +\n> +\t\t\tunsigned int bytesused = multiPlanar ? planes[0].bytesused\n> +\t\t\t\t\t       : buf.bytesused;\n> +\n> +\t\t\tfor (auto [i, plane] : utils::enumerate(buffer->planes())) {\n> +\t\t\t\tif (!bytesused) {\n> +\t\t\t\t\tLOG(V4L2, Error)\n> +\t\t\t\t\t\t<< \"Dequeued buffer is too small\";\n\nPresumably if we got here - it's likely something (hardware) already\noverwrote memory it likely didn't own?\n\n> +\t\t\t\t\treturn nullptr;\n\nI'm still very weary of returning nullptrs on dequeue when a buffer is\nremoved from queuedBuffers_\n\n\nWouldn't it be better to return the buffer, but mark it as corrupted or\ninvalid or such ?\n\n\n> +\t\t\t\t}\n> +\n> +\t\t\t\tmetadata.planes()[i].bytesused =\n> +\t\t\t\t\tstd::min(plane.length, bytesused);\n> +\t\t\t\tbytesused -= metadata.planes()[i].bytesused;\n> +\t\t\t}\n> +\t\t} else if (multiPlanar) {\n> +\t\t\t/*\n> +\t\t\t * If we use the multi-planar API, fill in the planes.\n> +\t\t\t * The number of planes in the frame buffer and in the\n> +\t\t\t * V4L2 buffer is guaranteed to be equal at this point.\n> +\t\t\t */\n> +\t\t\tfor (unsigned int i = 0; i < numV4l2Planes; ++i)\n> +\t\t\t\tmetadata.planes()[i].bytesused = planes[i].bytesused;\n> +\t\t} else {\n> +\t\t\tmetadata.planes()[0].bytesused = buf.bytesused;\n>  \t\t}\n> -\n> -\t\tfor (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> -\t\t\tbuffer->metadata_.planes()[nplane].bytesused = planes[nplane].bytesused;\n> -\t} else {\n> -\t\tbuffer->metadata_.planes()[0].bytesused = buf.bytesused;\n>  \t}\n>  \n>  \treturn buffer;\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 06E39BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 11:17:36 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 776476916B;\n\tThu,  2 Sep 2021 13:17:35 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0FDAE69166\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 13:17:34 +0200 (CEST)","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 83E1545E;\n\tThu,  2 Sep 2021 13:17:33 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Du5TQnLH\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630581453;\n\tbh=O9wSEGMlYlT5DK1dEU71LhUsBClqUAM6RIwR35V0lZM=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=Du5TQnLHsxXuDSI0NDwXW+Eog9T5bIpA9SjTLBvYoBS0Y2a4gX1I8b4UJwbRnAGGA\n\t6o7Yn/SeCdt1jdKtU0UEXSlUekQ5hCPuimqznRjzR1p+BpGIo6Z71Ugh5LDnVbl39u\n\tTxpH2cqfWXKAoQk7J5cZ3rjFFC2ugrH3TRRP7R6I=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-12-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<aad3bac8-de8a-f9a0-f590-0f9c26571dfb@ideasonboard.com>","Date":"Thu, 2 Sep 2021 12:17:31 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-12-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 11/12] libcamera:\n\tv4l2_videodevice: Split planes when dequeuing buffer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19284,"web_url":"https://patchwork.libcamera.org/comment/19284/","msgid":"<79a6c006-d7ed-2efa-1ff8-41d6b101fbb9@ideasonboard.com>","date":"2021-09-02T11:25:26","subject":"Re: [libcamera-devel] [RFC PATCH v1 10/12] libcamera:\n\tv4l2_videodevice: Coalesce planes when queuing buffer","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 05:23, Laurent Pinchart wrote:\n> When queueing a buffer to a V4L2VideoDevice, the number of planes in the\n> FrameBuffer may not match the number of V4L2 buffer planes if the\n> PixelFormat is multi-planar (has multiple colour planes) and the V4L2\n> format is single-planar (has a single buffer plane). In this case, we\n> need to coalesce all FrameBuffer planes into a single V4L2 buffer plane.\n> Do so, and add validity checks to reject frame buffers that can't be\n> described using a single V4L2 buffer plane.\n\n\nYikes ;-) lets see...\n\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  src/libcamera/v4l2_videodevice.cpp | 68 +++++++++++++++++++++++++-----\n>  1 file changed, 58 insertions(+), 10 deletions(-)\n> \n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 2d9a94c3c974..79cb792117d5 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -22,10 +22,12 @@\n>  \n>  #include <libcamera/base/event_notifier.h>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/utils.h>\n>  \n>  #include <libcamera/file_descriptor.h>\n>  \n>  #include \"libcamera/internal/formats.h\"\n> +#include \"libcamera/internal/framebuffer.h\"\n>  #include \"libcamera/internal/media_device.h\"\n>  #include \"libcamera/internal/media_object.h\"\n>  \n> @@ -1468,10 +1470,21 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n>  \n>  \tbool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type);\n>  \tconst std::vector<FrameBuffer::Plane> &planes = buffer->planes();\n> +\tunsigned int numV4l2Planes = format_.fourcc == formatInfo_->v4l2Format[0]\n> +\t\t\t\t   ? 1 : planes.size();\n\nAha, ok so that's how we recover the planar/multiplanar information I\nwas concerned we had lost earlier.\n\nI wonder if we should set/cache numV4l2Planes_ when we set formatInfo_\nas they are so closely paired .. But it's a trivial operation here.\n\n\n\n> +\n> +\t/*\n> +\t * If the frame buffer has multiple planes and the V4L2 format requires\n> +\t * contiguous planes, ensure that's the case.\n> +\t */\n> +\tif (planes.size() != numV4l2Planes && !buffer->_d()->isContiguous()) {\n> +\t\tLOG(V4L2, Error) << \"Device format requires contiguous buffer\";\n> +\t\treturn -EINVAL;\n> +\t}\n>  \n>  \tif (buf.memory == V4L2_MEMORY_DMABUF) {\n>  \t\tif (multiPlanar) {\n> -\t\t\tfor (unsigned int p = 0; p < planes.size(); ++p)\n> +\t\t\tfor (unsigned int p = 0; p < numV4l2Planes; ++p)\n>  \t\t\t\tv4l2Planes[p].m.fd = planes[p].fd.fd();\n>  \t\t} else {\n>  \t\t\tbuf.m.fd = planes[0].fd.fd();\n> @@ -1479,23 +1492,58 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n>  \t}\n>  \n>  \tif (multiPlanar) {\n> -\t\tbuf.length = planes.size();\n> +\t\tbuf.length = numV4l2Planes;\n>  \t\tbuf.m.planes = v4l2Planes;\n>  \t}\n>  \n>  \tif (V4L2_TYPE_IS_OUTPUT(buf.type)) {\n>  \t\tconst FrameMetadata &metadata = buffer->metadata();\n>  \n> -\t\tif (multiPlanar) {\n> -\t\t\tunsigned int nplane = 0;\n> -\t\t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n> -\t\t\t\tv4l2Planes[nplane].bytesused = plane.bytesused;\n> -\t\t\t\tv4l2Planes[nplane].length = buffer->planes()[nplane].length;\n> -\t\t\t\tnplane++;\n> +\t\tif (numV4l2Planes != planes.size()) {\n> +\t\t\t/*\n> +\t\t\t * If we have a multi-planar buffer with a V4L2\n> +\t\t\t * single-planar format, coalesce all planes. The length\n> +\t\t\t * and number of bytes used may only differ in the last\n> +\t\t\t * plane as any other situation can't be represented.\n> +\t\t\t */\n> +\t\t\tunsigned int bytesused = 0;\n> +\t\t\tunsigned int length = 0;\n> +\n> +\t\t\tfor (auto [i, plane] : utils::enumerate(planes)) {\n> +\t\t\t\tbytesused += metadata.planes()[i].bytesused;\n> +\t\t\t\tlength += plane.length;\n> +\n> +\t\t\t\tif (i != planes.size() - 1 && bytesused != length) {\n\nAh, i almost thought that was wrong, but it's\n   \"If we're not the last plane, and the plane is not fully used...\"\n\nSo it's fine.\n\n\n> +\t\t\t\t\tLOG(V4L2, Error)\n> +\t\t\t\t\t\t<< \"Holes in multi-planar buffer not supported\";\n> +\t\t\t\t\treturn -EINVAL;\n> +\t\t\t\t}\n> +\t\t\t}\n> +\n> +\t\t\tif (multiPlanar) {\n> +\t\t\t\tv4l2Planes[0].bytesused = bytesused;\n> +\t\t\t\tv4l2Planes[0].length = length;\n> +\t\t\t} else {\n> +\t\t\t\tbuf.bytesused = bytesused;\n> +\t\t\t\tbuf.length = length;\n> +\t\t\t}\n> +\t\t} else if (multiPlanar) {\n> +\t\t\t/*\n> +\t\t\t * If we use the multi-planar API, fill in the planes.\n> +\t\t\t * The number of planes in the frame buffer and in the\n> +\t\t\t * V4L2 buffer is guaranteed to be equal at this point.\n> +\t\t\t */\n> +\t\t\tfor (auto [i, plane] : utils::enumerate(planes)) {\n> +\t\t\t\tv4l2Planes[i].bytesused = metadata.planes()[i].bytesused;\n> +\t\t\t\tv4l2Planes[i].length = plane.length;\n>  \t\t\t}\n>  \t\t} else {\n> -\t\t\tif (metadata.planes().size())\n\nGuaranteeing the planes are there is much nicer here.\n\nPhew, this patch 'looks' a lot more complex than it actually is,\n\nSo I think ... this is fine.\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> -\t\t\t\tbuf.bytesused = metadata.planes()[0].bytesused;\n> +\t\t\t/*\n> +\t\t\t * Single-planar API with a single plane in the buffer\n> +\t\t\t * is trivial to handle.\n> +\t\t\t */\n> +\t\t\tbuf.bytesused = metadata.planes()[0].bytesused;\n> +\t\t\tbuf.length = planes[0].length;\n>  \t\t}\n>  \n>  \t\tbuf.sequence = metadata.sequence;\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 10597BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 11:25:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6B6B869166;\n\tThu,  2 Sep 2021 13:25:30 +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 A4CAF60255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 13:25:28 +0200 (CEST)","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 2324A45E;\n\tThu,  2 Sep 2021 13:25:28 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"dUEH2g17\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630581928;\n\tbh=gE/8gcbqLSWKKxJE0uLp8jpAKxb00qhrFi6JcCvoEA0=;\n\th=To:References:From:Subject:Date:In-Reply-To:From;\n\tb=dUEH2g17Q+UDKmG6wf9dxRbwh30LxYek/J3tB8NIn4wf3JMEX11DfTyOEyUowxH1m\n\tWVRQSsVt1bL5BbXqADQEReYzP8U58AAnwyiuzIf6w7BwRD/LzkAv+5fs3jJy+jR7M3\n\tBqMUcCm5R0li/d23opiYs8/pZuvYn2SiOorDkBhU=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-11-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<79a6c006-d7ed-2efa-1ff8-41d6b101fbb9@ideasonboard.com>","Date":"Thu, 2 Sep 2021 12:25:26 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<20210902042303.2254-11-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 10/12] libcamera:\n\tv4l2_videodevice: Coalesce planes when queuing buffer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19303,"web_url":"https://patchwork.libcamera.org/comment/19303/","msgid":"<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>","date":"2021-09-02T18:07:58","subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","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, Sep 02, 2021 at 10:43:56AM +0100, Kieran Bingham wrote:\n> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > Multi-planar frame buffers can store their planes contiguously in\n> > memory, or split them in discontiguous memory areas. Add a private\n> > function to check in which of these two categories the frame buffer\n> > belongs. This will be used to correctly handle the differences between\n> > the V4L2 single and multi planar APIs.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/internal/framebuffer.h |  2 ++\n> >  src/libcamera/framebuffer.cpp            | 45 +++++++++++++++++++++++-\n> >  2 files changed, 46 insertions(+), 1 deletion(-)\n> > \n> > diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h\n> > index 606aed2b4782..cd33c295466e 100644\n> > --- a/include/libcamera/internal/framebuffer.h\n> > +++ b/include/libcamera/internal/framebuffer.h\n> > @@ -21,9 +21,11 @@ public:\n> >  \tPrivate();\n> >  \n> >  \tvoid setRequest(Request *request) { request_ = request; }\n> > +\tbool isContiguous() const { return isContiguous_; }\n> >  \n> >  private:\n> >  \tRequest *request_;\n> > +\tbool isContiguous_;\n> >  };\n> >  \n> >  } /* namespace libcamera */\n> > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > index 53ef89bf458f..99265b44da43 100644\n> > --- a/src/libcamera/framebuffer.cpp\n> > +++ b/src/libcamera/framebuffer.cpp\n> > @@ -106,7 +106,7 @@ LOG_DEFINE_CATEGORY(Buffer)\n> >   */\n> >  \n> >  FrameBuffer::Private::Private()\n> > -\t: request_(nullptr)\n> > +\t: request_(nullptr), isContiguous_(true)\n> >  {\n> >  }\n> >  \n> > @@ -120,6 +120,17 @@ FrameBuffer::Private::Private()\n> >   * handlers, it is called by the pipeline handlers themselves.\n> >   */\n> >  \n> > +/**\n> > + * \\fn FrameBuffer::Private::isContiguous()\n> > + * \\brief Check if the frame buffer stores planes contiguously in memory\n> > + *\n> > + * Multi-planar frame buffers can store their planes contiguously in memory, or\n> > + * split them in discontiguous memory areas. This function checks in which of\n> \n> 'split them into discontiguous'\n> \n> > + * these two categories the frame buffer belongs.\n> > + *\n> > + * \\return True if the planes are stored contiguously in memory, false otherwise\n> > + */\n> > +\n> >  /**\n> >   * \\class FrameBuffer\n> >   * \\brief Frame buffer data and its associated dynamic metadata\n> > @@ -199,9 +210,41 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> >  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n> >  \t  cookie_(cookie)\n> >  {\n> > +\tunsigned int offset = 0;\n> > +\n> >  \t/* \\todo Remove the assertions after sufficient testing */\n> >  \tfor (const auto &plane : planes_)\n> >  \t\tASSERT(plane.offset != Plane::kInvalidOffset);\n> > +\n> > +\tbool isContiguous = true;\n> > +\tino_t inode = 0;\n> > +\n> > +\tfor (const auto &plane : planes_) {\n> > +\t\tif (plane.offset != offset) {\n> > +\t\t\tisContiguous = false;\n> > +\t\t\tbreak;\n> > +\t\t}\n> > +\n> > +\t\t/*\n> > +\t\t * Two different dmabuf file descriptors may still refer to the\n> > +\t\t * same dmabuf instance. Check this using inodes.\n> \n> Going back to the FileDescriptor::inode() extension, if that cached the\n> inode (it can't change) on either first call, or construction, couldn't\n> this whole check be simplified to:\n> \n> \t\tif (plane.fd.inode() != planes_[0].fd.inode()) {\n> \t\t\tisContiguous = false;\n> \t\t\tbreak;\n> \t\t}\n\nThis would call fstat() every time, while the fd comparison here is\nmeant to skip the fstat() calls in case the numerical fd match. We could\ndo\n\n\t\tif (plane.fd != planes_[0].fd)\n\nif we introduced an operator==(), but as explained in a reply to the\npatch that adds inode(), I'd like to handle that later, to avoid\nblocking this series.\n\n> > +\t\t */\n> > +\t\tif (plane.fd.fd() != planes_[0].fd.fd()) {\n> > +\t\t\tif (!inode)\n> > +\t\t\t\tinode = planes_[0].fd.inode();\n> > +\t\t\tif (plane.fd.inode() != inode) {\n> > +\t\t\t\tisContiguous = false;\n> > +\t\t\t\tbreak;\n> > +\t\t\t}\n> > +\t\t}\n> > +\n> > +\t\toffset += plane.length;\n> > +\t}\n> > +\n> > +\tLOG(Buffer, Debug)\n> > +\t\t<< \"Buffer is \" << (isContiguous ? \"not \" : \"\") << \"contiguous\";\n> > +\n> > +\t_d()->isContiguous_ = isContiguous;\n> >  }\n> >  \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 053CCBDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 18:08:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BF77A6916A;\n\tThu,  2 Sep 2021 20:08:16 +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 42C3660255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 20:08:15 +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 A980F291;\n\tThu,  2 Sep 2021 20:08:14 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"kNKZugW5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630606094;\n\tbh=63XwOLr4TeoB7NamsnsZ/97sfX38I6qO0LVxVf4mlOg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=kNKZugW5uJ8yoW3bRMoEzXjdY2u5/V9a61IPOddlTAAG9+pRsj94xNHlBX66MGZoN\n\tJcDUUtI0J2ic4kqlZX2K18TzW1VVUi6QgLF8CKwdKtPwtR5bE+UBkJRvZ5jkuk+aAE\n\t6AxeFdXyWGmbFBRIeNrBPahCZM2FFdnXAXDsjqO0=","Date":"Thu, 2 Sep 2021 21:07:58 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>\n\t<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19304,"web_url":"https://patchwork.libcamera.org/comment/19304/","msgid":"<YTET10o5uRrgsrEm@pendragon.ideasonboard.com>","date":"2021-09-02T18:11:35","subject":"Re: [libcamera-devel] [RFC PATCH v1 07/12] libcamera: framebuffer:\n\tAllocate metadata planes at construction time","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, Sep 02, 2021 at 10:54:19AM +0100, Kieran Bingham wrote:\n> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > The metadata planes are allocated by V4L2VideoDevice when dequeuing a\n> > buffer. This causes the metadata planes to only be allocated after a\n> > buffer gets dequeued, and doesn't provide any strong guarantee that\n> > their number matches the number of FrameBuffer planes. The lack of this\n> > invariant makes the FrameBuffer class fragile.\n> > \n> > As a first step towards fixing this, allocate the metadata planes when\n> > the FrameBuffer is constructed. The FrameMetadata API should be further\n> > improved by preventing a change in the number of planes.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  src/libcamera/framebuffer.cpp      |  2 ++\n> >  src/libcamera/v4l2_videodevice.cpp | 12 +++++++++---\n> >  2 files changed, 11 insertions(+), 3 deletions(-)\n> > \n> > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > index 99265b44da43..c1529dfb0ad1 100644\n> > --- a/src/libcamera/framebuffer.cpp\n> > +++ b/src/libcamera/framebuffer.cpp\n> > @@ -210,6 +210,8 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> >  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n> >  \t  cookie_(cookie)\n> >  {\n> > +\tmetadata_.planes.resize(planes_.size());\n> > +\n> >  \tunsigned int offset = 0;\n> >  \n> >  \t/* \\todo Remove the assertions after sufficient testing */\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index adabd4720668..a51971879e75 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -1586,12 +1586,18 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n> >  \tbuffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> >  \t\t\t\t    + buf.timestamp.tv_usec * 1000ULL;\n> >  \n> > -\tbuffer->metadata_.planes.clear();\n> >  \tif (multiPlanar) {\n> > +\t\tif (buf.length > buffer->metadata_.planes.size()) {\n> > +\t\t\tLOG(V4L2, Error)\n> \n> Are we going to 'leak' queued FrameBuffers at this point?\n> Is there a way to prevent this occurring at queue time rather than dequeue?\n> \n> In fact, Can it even happen? or should it be an ASSERT/Fatal?\n\nIt's not supposed to happen, I'd say it's a kernel bug in that case. But\nit's a good point, I think we can return the buffer, and mark its status\nas erroneous. I'll do so.\n\n> > +\t\t\t\t<< \"Invalid number of planes (\" << buf.length\n> > +\t\t\t\t<< \" != \" << buffer->metadata_.planes.size() << \")\";\n> > +\t\t\treturn nullptr;\n> > +\t\t}\n> > +\n> >  \t\tfor (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> > -\t\t\tbuffer->metadata_.planes.push_back({ planes[nplane].bytesused });\n> > +\t\t\tbuffer->metadata_.planes[nplane].bytesused = planes[nplane].bytesused;\n> >  \t} else {\n> > -\t\tbuffer->metadata_.planes.push_back({ buf.bytesused });\n> > +\t\tbuffer->metadata_.planes[0].bytesused = buf.bytesused;\n> >  \t}\n> >  \n> >  \treturn buffer;\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 30A17BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 18:11:55 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 98B566916A;\n\tThu,  2 Sep 2021 20:11:54 +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 5E58460255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 20:11:52 +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 CC08D45E;\n\tThu,  2 Sep 2021 20:11:51 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"I8BDUvNQ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630606312;\n\tbh=mr8ncv5da1qYnw+zQ+JeO+9OmEcF0SQxmMZwomHycmE=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=I8BDUvNQRa7iyuYeEhyAau8onXimYPwOzsST8XnjfX15RAaVkfNJPijHdSgCjcRu6\n\tmUqO62TUT9qVAWp/shE+I6hzdQvwI3x3SBf84m1zySKKVNK2dEFVxa/7orBgqiOWnR\n\tIs4Y6J92TZHpcrRBZrz+VOBTYIHkFtb3VK676p8M=","Date":"Thu, 2 Sep 2021 21:11:35 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTET10o5uRrgsrEm@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-8-laurent.pinchart@ideasonboard.com>\n\t<a36b82c0-e1fe-9cba-0eb7-814391fc68fe@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<a36b82c0-e1fe-9cba-0eb7-814391fc68fe@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 07/12] libcamera: framebuffer:\n\tAllocate metadata planes at construction time","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19305,"web_url":"https://patchwork.libcamera.org/comment/19305/","msgid":"<YTEV40OWld/Oxe4J@pendragon.ideasonboard.com>","date":"2021-09-02T18:20:19","subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","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, Sep 02, 2021 at 11:03:47AM +0100, Kieran Bingham wrote:\n> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > The number of metadata planes should always match the number of frame\n> > buffer planes. Enforce this by making the vector private and providing\n> > accessor functions.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/framebuffer.h    | 10 +++++++++-\n> >  src/cam/camera_session.cpp         |  4 ++--\n> >  src/cam/file_sink.cpp              |  2 +-\n> >  src/libcamera/framebuffer.cpp      | 12 +++++++++---\n> >  src/libcamera/v4l2_videodevice.cpp | 14 +++++++-------\n> >  src/qcam/main_window.cpp           |  2 +-\n> >  src/qcam/viewfinder_gl.cpp         |  2 +-\n> >  src/qcam/viewfinder_qt.cpp         |  2 +-\n> >  src/v4l2/v4l2_camera_proxy.cpp     |  2 +-\n> >  9 files changed, 32 insertions(+), 18 deletions(-)\n> > \n> > diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n> > index fd68ed0a139d..7f2f176af691 100644\n> > --- a/include/libcamera/framebuffer.h\n> > +++ b/include/libcamera/framebuffer.h\n> > @@ -13,6 +13,7 @@\n> >  #include <vector>\n> >  \n> >  #include <libcamera/base/class.h>\n> > +#include <libcamera/base/span.h>\n> >  \n> >  #include <libcamera/file_descriptor.h>\n> >  \n> > @@ -34,7 +35,14 @@ struct FrameMetadata {\n> >  \tStatus status;\n> >  \tunsigned int sequence;\n> >  \tuint64_t timestamp;\n> > -\tstd::vector<Plane> planes;\n> > +\n> > +\tSpan<Plane> planes() { return planes_; }\n> > +\tSpan<const Plane> planes() const { return planes_; }\n> \n> Returning a span here is nice.\n> \n> This likely causes compile breakage for any external app.\n> \n> I know we're not ABI/API stable, but I wonder if we should highlight\n> when we do cause breakage somehow, perhaps in the commit message at\n> least, as we already have external users who we might want to notify.\n\nMost changes to public header files will be ABI/API breakages at this\npoint, and users will certainly notice when they get a compilation\nfailure :-) What value do you think this would bring, who would read\nthose messages ? I think it could be different for things that change\nthe ABI without breaking compilation and that would require more than a\nrecompilation to fix, there a warning seems to have more value, but I'm\nalso sure we'll forget from time to time, if not most of the time.\n\n> > +\n> > +private:\n> > +\tfriend class FrameBuffer;\n> > +\n> > +\tstd::vector<Plane> planes_;\n> >  };\n> >  \n> >  class FrameBuffer final : public Extensible\n> > diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\n> > index 60d640f2b15c..32a373a99b72 100644\n> > --- a/src/cam/camera_session.cpp\n> > +++ b/src/cam/camera_session.cpp\n> > @@ -374,9 +374,9 @@ void CameraSession::processRequest(Request *request)\n> >  \t\t     << \" bytesused: \";\n> >  \n> >  \t\tunsigned int nplane = 0;\n> > -\t\tfor (const FrameMetadata::Plane &plane : metadata.planes) {\n> > +\t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n> >  \t\t\tinfo << plane.bytesused;\n> > -\t\t\tif (++nplane < metadata.planes.size())\n> > +\t\t\tif (++nplane < metadata.planes().size())\n> >  \t\t\t\tinfo << \"/\";\n> >  \t\t}\n> >  \t}\n> > diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\n> > index 0b529e3eb767..0fc7d621f50b 100644\n> > --- a/src/cam/file_sink.cpp\n> > +++ b/src/cam/file_sink.cpp\n> > @@ -110,7 +110,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)\n> >  \n> >  \tfor (unsigned int i = 0; i < buffer->planes().size(); ++i) {\n> >  \t\tconst FrameBuffer::Plane &plane = buffer->planes()[i];\n> > -\t\tconst FrameMetadata::Plane &meta = buffer->metadata().planes[i];\n> > +\t\tconst FrameMetadata::Plane &meta = buffer->metadata().planes()[i];\n> >  \n> >  \t\tuint8_t *data = planeData_[&plane];\n> >  \t\tunsigned int length = std::min(meta.bytesused, plane.length);\n> > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > index c1529dfb0ad1..83cf7b83d182 100644\n> > --- a/src/libcamera/framebuffer.cpp\n> > +++ b/src/libcamera/framebuffer.cpp\n> > @@ -91,8 +91,14 @@ LOG_DEFINE_CATEGORY(Buffer)\n> >   */\n> >  \n> >  /**\n> > - * \\var FrameMetadata::planes\n> > - * \\brief Array of per-plane metadata\n> > + * \\fn FrameMetadata::planes()\n> > + * \\copydoc FrameMetadata::planes() const\n> > + */\n> > +\n> > +/**\n> > + * \\fn FrameMetadata::planes() const\n> > + * \\brief Retrieve the array of per-plane metadata\n> > + * \\return The array of per-plane metadata\n> >   */\n> >  \n> >  /**\n> > @@ -210,7 +216,7 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> >  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n> >  \t  cookie_(cookie)\n> >  {\n> > -\tmetadata_.planes.resize(planes_.size());\n> > +\tmetadata_.planes_.resize(planes_.size());\n> >  \n> >  \tunsigned int offset = 0;\n> >  \n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index a51971879e75..82ddaed3656f 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -1485,14 +1485,14 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n> >  \n> >  \t\tif (multiPlanar) {\n> >  \t\t\tunsigned int nplane = 0;\n> > -\t\t\tfor (const FrameMetadata::Plane &plane : metadata.planes) {\n> > +\t\t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n> >  \t\t\t\tv4l2Planes[nplane].bytesused = plane.bytesused;\n> >  \t\t\t\tv4l2Planes[nplane].length = buffer->planes()[nplane].length;\n> >  \t\t\t\tnplane++;\n> >  \t\t\t}\n> >  \t\t} else {\n> > -\t\t\tif (metadata.planes.size())\n> > -\t\t\t\tbuf.bytesused = metadata.planes[0].bytesused;\n> > +\t\t\tif (metadata.planes().size())\n> > +\t\t\t\tbuf.bytesused = metadata.planes()[0].bytesused;\n> >  \t\t}\n> >  \n> >  \t\tbuf.sequence = metadata.sequence;\n> > @@ -1587,17 +1587,17 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n> >  \t\t\t\t    + buf.timestamp.tv_usec * 1000ULL;\n> >  \n> >  \tif (multiPlanar) {\n> > -\t\tif (buf.length > buffer->metadata_.planes.size()) {\n> > +\t\tif (buf.length > buffer->metadata_.planes().size()) {\n> >  \t\t\tLOG(V4L2, Error)\n> >  \t\t\t\t<< \"Invalid number of planes (\" << buf.length\n> > -\t\t\t\t<< \" != \" << buffer->metadata_.planes.size() << \")\";\n> > +\t\t\t\t<< \" != \" << buffer->metadata_.planes().size() << \")\";\n> >  \t\t\treturn nullptr;\n> >  \t\t}\n> >  \n> >  \t\tfor (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> > -\t\t\tbuffer->metadata_.planes[nplane].bytesused = planes[nplane].bytesused;\n> > +\t\t\tbuffer->metadata_.planes()[nplane].bytesused = planes[nplane].bytesused;\n> >  \t} else {\n> > -\t\tbuffer->metadata_.planes[0].bytesused = buf.bytesused;\n> > +\t\tbuffer->metadata_.planes()[0].bytesused = buf.bytesused;\n> >  \t}\n> >  \n> >  \treturn buffer;\n> > diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> > index 1536b2b5bd66..ac853e360aea 100644\n> > --- a/src/qcam/main_window.cpp\n> > +++ b/src/qcam/main_window.cpp\n> > @@ -756,7 +756,7 @@ void MainWindow::processViewfinder(FrameBuffer *buffer)\n> >  \n> >  \tqDebug().noquote()\n> >  \t\t<< QString(\"seq: %1\").arg(metadata.sequence, 6, 10, QLatin1Char('0'))\n> > -\t\t<< \"bytesused:\" << metadata.planes[0].bytesused\n> > +\t\t<< \"bytesused:\" << metadata.planes()[0].bytesused\n> >  \t\t<< \"timestamp:\" << metadata.timestamp\n> >  \t\t<< \"fps:\" << Qt::fixed << qSetRealNumberPrecision(2) << fps;\n> >  \n> > diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n> > index 40226601f9fd..d2ef036974f4 100644\n> > --- a/src/qcam/viewfinder_gl.cpp\n> > +++ b/src/qcam/viewfinder_gl.cpp\n> > @@ -125,7 +125,7 @@ void ViewFinderGL::render(libcamera::FrameBuffer *buffer,\n> >  \t/*\n> >  \t * \\todo Get the stride from the buffer instead of computing it naively\n> >  \t */\n> > -\tstride_ = buffer->metadata().planes[0].bytesused / size_.height();\n> > +\tstride_ = buffer->metadata().planes()[0].bytesused / size_.height();\n> \n> Can this be obtained from the PixelFormatInfo now ?\n> or do we still not expose that to applications...\n\nPixelFormatInfo is still internal. I'm considering exposing it, but then\nit would be good to move the V4L2-specific part somewhere else.\n\n> Anyway, that's likely a change on top even if we could to solve the \\todo.\n> \n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> >  \tupdate();\n> >  \tbuffer_ = buffer;\n> >  }\n> > diff --git a/src/qcam/viewfinder_qt.cpp b/src/qcam/viewfinder_qt.cpp\n> > index efa1d412584b..a0bf99b0b522 100644\n> > --- a/src/qcam/viewfinder_qt.cpp\n> > +++ b/src/qcam/viewfinder_qt.cpp\n> > @@ -87,7 +87,7 @@ void ViewFinderQt::render(libcamera::FrameBuffer *buffer,\n> >  \t}\n> >  \n> >  \tunsigned char *memory = mem.data();\n> > -\tsize_t size = buffer->metadata().planes[0].bytesused;\n> > +\tsize_t size = buffer->metadata().planes()[0].bytesused;\n> >  \n> >  \t{\n> >  \t\tQMutexLocker locker(&mutex_);\n> > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> > index 8d8ee395954f..68e47ee81834 100644\n> > --- a/src/v4l2/v4l2_camera_proxy.cpp\n> > +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> > @@ -212,7 +212,7 @@ void V4L2CameraProxy::updateBuffers()\n> >  \n> >  \t\tswitch (fmd.status) {\n> >  \t\tcase FrameMetadata::FrameSuccess:\n> > -\t\t\tbuf.bytesused = fmd.planes[0].bytesused;\n> > +\t\t\tbuf.bytesused = fmd.planes()[0].bytesused;\n> >  \t\t\tbuf.field = V4L2_FIELD_NONE;\n> >  \t\t\tbuf.timestamp.tv_sec = fmd.timestamp / 1000000000;\n> >  \t\t\tbuf.timestamp.tv_usec = fmd.timestamp % 1000000;","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 70C04BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 18:20:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AE6B06916D;\n\tThu,  2 Sep 2021 20:20:36 +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 EF71160255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 20:20:35 +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 6A58B45E;\n\tThu,  2 Sep 2021 20:20:35 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"uoMzpiSU\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630606835;\n\tbh=4nAju0ACxxFb6096EF1tKFF4WfYIUSay4/h4SNHWWy8=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=uoMzpiSU2CiifWG6gtPQGFmr4TC39rdZy83FzPYsLf3teoneJakMxMm0kaezLQMWD\n\tZhS9X84oLge2+27JydjQIjzG9adPo/JvhEfVZxRdJ0SHfUjNQ+pxIkclynDxC8G7gQ\n\tT1ZtHTwAAoLaZm4u/4BBx1KmuDfwR5iWhrXnbUSA=","Date":"Thu, 2 Sep 2021 21:20:19 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTEV40OWld/Oxe4J@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-9-laurent.pinchart@ideasonboard.com>\n\t<23fb7400-0ebd-276b-52f9-863442ebf4b1@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<23fb7400-0ebd-276b-52f9-863442ebf4b1@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19306,"web_url":"https://patchwork.libcamera.org/comment/19306/","msgid":"<YTEXaTYw2x9VxCQQ@pendragon.ideasonboard.com>","date":"2021-09-02T18:26:49","subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","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, Sep 02, 2021 at 11:21:29AM +0100, Kieran Bingham wrote:\n> On 02/09/2021 05:23, Laurent Pinchart wrote:\n> > Cache the PixelFormatInfo instead of looking it up in every call to\n> > createBuffer(). This prepare for usage of the info in queueBuffer(), to\n> \n> s/prepare/prepares/\n> \n> > avoid a looking every time a buffer is queued.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/internal/v4l2_videodevice.h |  1 +\n> >  src/libcamera/v4l2_videodevice.cpp            | 19 +++++++++++--------\n> >  2 files changed, 12 insertions(+), 8 deletions(-)\n> > \n> > diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h\n> > index 7a145f608a5b..6096cd609b97 100644\n> > --- a/include/libcamera/internal/v4l2_videodevice.h\n> > +++ b/include/libcamera/internal/v4l2_videodevice.h\n> > @@ -243,6 +243,7 @@ private:\n> >  \n> >  \tV4L2Capability caps_;\n> >  \tV4L2DeviceFormat format_;\n> > +\tconst PixelFormatInfo *formatInfo_;\n> >  \n> >  \tenum v4l2_buf_type bufferType_;\n> >  \tenum v4l2_memory memoryType_;\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 82ddaed3656f..2d9a94c3c974 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -482,8 +482,8 @@ const std::string V4L2DeviceFormat::toString() const\n> >   * \\param[in] deviceNode The file-system path to the video device node\n> >   */\n> >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > -\t: V4L2Device(deviceNode), cache_(nullptr), fdBufferNotifier_(nullptr),\n> > -\t  streaming_(false)\n> > +\t: V4L2Device(deviceNode), formatInfo_(nullptr), cache_(nullptr),\n> > +\t  fdBufferNotifier_(nullptr), streaming_(false)\n> >  {\n> >  \t/*\n> >  \t * We default to an MMAP based CAPTURE video device, however this will\n> > @@ -586,6 +586,7 @@ int V4L2VideoDevice::open()\n> >  \t\treturn ret;\n> >  \t}\n> >  \n> > +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> \n> I shudder a little seeing code hugging the return like that ;-)\n> \n> But it's purely subjective, same below of course.\n\nWe have various patterns indeed :-) I haven't been able to establish yet\nwhat my mental process to deal with these are, sometimes it bothers me\ntoo, but not always.\n\n> >  \treturn 0;\n> >  }\n> >  \n> > @@ -681,6 +682,7 @@ int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)\n> >  \t\treturn ret;\n> >  \t}\n> >  \n> > +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> >  \treturn 0;\n> >  }\n> >  \n> > @@ -695,6 +697,8 @@ void V4L2VideoDevice::close()\n> >  \treleaseBuffers();\n> >  \tdelete fdBufferNotifier_;\n> >  \n> > +\tformatInfo_ = nullptr;\n> > +\n> >  \tV4L2Device::close();\n> >  }\n> >  \n> > @@ -787,6 +791,7 @@ int V4L2VideoDevice::setFormat(V4L2DeviceFormat *format)\n> >  \t\treturn ret;\n> >  \n> >  \tformat_ = *format;\n> > +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> \n> Looks like we were already using that pattern though ;-)\n> \n> >  \treturn 0;\n> >  }\n> >  \n> > @@ -1325,19 +1330,17 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> >  \t\tplanes.push_back(std::move(plane));\n> >  \t}\n> >  \n> > -\tconst auto &info = PixelFormatInfo::info(format_.fourcc);\n> > -\tif (info.isValid() && info.numPlanes() != numPlanes) {\n> > +\tif (formatInfo_->isValid() && formatInfo_->numPlanes() != numPlanes) {\n> >  \t\tASSERT(numPlanes == 1u);\n> \n> Not for this patch, but I guess this assert needed a\n>   \\todo Multiplanar support\n> \n> around it?\n\nNo, when formatInfo_->numPlanes() != numPlanes, it means that we have a\nmulti-planar FrameBuffer with a single-planar V4L2 buffer format (e.g.\nV4L2_PIX_FMT_NV12 as opposed to V4L2_PIX_FMT_NV12M). numPlanes must be 1\nin that case, otherwise it's a kernel bug.\n\n> However, this patch is just for caching the formatInfo, which seems\n> reasonable so\n> \n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> > -\t\tconst size_t numColorPlanes = info.numPlanes();\n> > -\t\tplanes.resize(numColorPlanes);\n> > +\t\tplanes.resize(formatInfo_->numPlanes());\n> >  \t\tconst FileDescriptor &fd = planes[0].fd;\n> >  \t\tsize_t offset = 0;\n> > -\t\tfor (size_t i = 0; i < numColorPlanes; ++i) {\n> > +\t\tfor (size_t i = 0; i < planes.size(); ++i) {\n> >  \t\t\tplanes[i].fd = fd;\n> >  \t\t\tplanes[i].offset = offset;\n> >  \n> >  \t\t\t/* \\todo Take the V4L2 stride into account */\n> > -\t\t\tplanes[i].length = info.planeSize(format_.size, i);\n> > +\t\t\tplanes[i].length = formatInfo_->planeSize(format_.size, i);\n> >  \t\t\toffset += planes[i].length;\n> >  \t\t}\n> >  \t}","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 853B3BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 18:27:08 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 000FD69166;\n\tThu,  2 Sep 2021 20:27:07 +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 6D83060255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 20:27:06 +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 DAAEB45E;\n\tThu,  2 Sep 2021 20:27:05 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"I5s/at3W\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630607226;\n\tbh=1UopcddfPWTqin6FE1xc0U++5sDJFRmbOP+0hoV8G/o=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=I5s/at3WOEX8NCMF6S3rkTm7WWdDQZSStk0ipbo4qQuqCUs2ZGzm6aBo5+sABG4cs\n\t5SUkaHByOs8ljXdileRzhgmAn8l49GALavmn8lac/LxV2tGJGWThKABqtmJdm1gQPw\n\tU5kEJfDsIZz2UlQ3tXei2fo0btzNx1bvev0VF9io=","Date":"Thu, 2 Sep 2021 21:26:49 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTEXaTYw2x9VxCQQ@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-10-laurent.pinchart@ideasonboard.com>\n\t<ef9325cc-41f1-efa3-109d-0e5dd238317a@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<ef9325cc-41f1-efa3-109d-0e5dd238317a@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19307,"web_url":"https://patchwork.libcamera.org/comment/19307/","msgid":"<YTEYccqJgkR/A4Id@pendragon.ideasonboard.com>","date":"2021-09-02T18:31:13","subject":"Re: [libcamera-devel] [RFC PATCH v1 11/12] libcamera:\n\tv4l2_videodevice: Split planes when dequeuing buffer","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, Sep 02, 2021 at 12:17:31PM +0100, Kieran Bingham wrote:\n> On 02/09/2021 05:23, Laurent Pinchart wrote:\n> > When dequeueing a buffer from a V4L2VideoDevice, the number of planes in\n> > the FrameBuffer may not match the number of V4L2 buffer planes if the\n> > PixelFormat is multi-planar (has multiple colour planes) and the V4L2\n> > format is single-planar (has a single buffer plane). In this case, we\n> > need to split the single V4L2 buffer plane into FrameBuffer planes. Do\n> > so, and add checks to reject invalid V4L2 buffers in case of a driver\n> > issue.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  src/libcamera/v4l2_videodevice.cpp | 54 ++++++++++++++++++++++++------\n> >  1 file changed, 43 insertions(+), 11 deletions(-)\n> > \n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 79cb792117d5..b80b9038a914 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -7,6 +7,7 @@\n> >  \n> >  #include \"libcamera/internal/v4l2_videodevice.h\"\n> >  \n> > +#include <algorithm>\n> >  #include <array>\n> >  #include <fcntl.h>\n> >  #include <iomanip>\n> > @@ -1637,18 +1638,49 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n> >  \tbuffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> >  \t\t\t\t    + buf.timestamp.tv_usec * 1000ULL;\n> >  \n> > -\tif (multiPlanar) {\n> > -\t\tif (buf.length > buffer->metadata_.planes().size()) {\n> > -\t\t\tLOG(V4L2, Error)\n> > -\t\t\t\t<< \"Invalid number of planes (\" << buf.length\n> > -\t\t\t\t<< \" != \" << buffer->metadata_.planes().size() << \")\";\n> > -\t\t\treturn nullptr;\n> > +\tif (V4L2_TYPE_IS_CAPTURE(buf.type)) {\n> \n> Should we swap this around and return the buffer early to lower the\n> indents below?\n> \n>  if (!V4L2_TYPE_IS_CAPTURE(buf.type)) {\n> \t/* Output buffer is completed */\n> \treturn buffer;\n>  }\n\nYes, good point.\n\n> > +\t\tunsigned int numV4l2Planes = multiPlanar ? buf.length : 1;\n> > +\t\tFrameMetadata &metadata = buffer->metadata_;\n> > +\n> > +\t\tif (numV4l2Planes != metadata.planes().size()) {\n> > +\t\t\t/*\n> > +\t\t\t * If we have a multi-planar buffer with a V4L2\n> > +\t\t\t * single-planar format, split the V4L2 buffer across\n> > +\t\t\t * the buffer planes. Only the last plane may have less\n> > +\t\t\t * bytes used than its length.\n> > +\t\t\t */\n> > +\t\t\tif (numV4l2Planes != 1) {\n> > +\t\t\t\tLOG(V4L2, Error)\n> > +\t\t\t\t\t<< \"Invalid number of planes (\" << numV4l2Planes\n> > +\t\t\t\t\t<< \" != \" << metadata.planes().size() << \")\";\n> > +\t\t\t\treturn nullptr;\n> > +\t\t\t}\n> > +\n> > +\t\t\tunsigned int bytesused = multiPlanar ? planes[0].bytesused\n> > +\t\t\t\t\t       : buf.bytesused;\n> > +\n> > +\t\t\tfor (auto [i, plane] : utils::enumerate(buffer->planes())) {\n> > +\t\t\t\tif (!bytesused) {\n> > +\t\t\t\t\tLOG(V4L2, Error)\n> > +\t\t\t\t\t\t<< \"Dequeued buffer is too small\";\n> \n> Presumably if we got here - it's likely something (hardware) already\n> overwrote memory it likely didn't own?\n\nIt likely means something bad happened, yes. It's a bit of defensive\nprogramming, to output a clear error message if the kernel does\nsomething bad, but also to possibly catch bugs in the implementation of\nthis function.\n\n> > +\t\t\t\t\treturn nullptr;\n> \n> I'm still very weary of returning nullptrs on dequeue when a buffer is\n> removed from queuedBuffers_\n> \n> \n> Wouldn't it be better to return the buffer, but mark it as corrupted or\n> invalid or such ?\n\nYes, I'll do that.\n\n> > +\t\t\t\t}\n> > +\n> > +\t\t\t\tmetadata.planes()[i].bytesused =\n> > +\t\t\t\t\tstd::min(plane.length, bytesused);\n> > +\t\t\t\tbytesused -= metadata.planes()[i].bytesused;\n> > +\t\t\t}\n> > +\t\t} else if (multiPlanar) {\n> > +\t\t\t/*\n> > +\t\t\t * If we use the multi-planar API, fill in the planes.\n> > +\t\t\t * The number of planes in the frame buffer and in the\n> > +\t\t\t * V4L2 buffer is guaranteed to be equal at this point.\n> > +\t\t\t */\n> > +\t\t\tfor (unsigned int i = 0; i < numV4l2Planes; ++i)\n> > +\t\t\t\tmetadata.planes()[i].bytesused = planes[i].bytesused;\n> > +\t\t} else {\n> > +\t\t\tmetadata.planes()[0].bytesused = buf.bytesused;\n> >  \t\t}\n> > -\n> > -\t\tfor (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> > -\t\t\tbuffer->metadata_.planes()[nplane].bytesused = planes[nplane].bytesused;\n> > -\t} else {\n> > -\t\tbuffer->metadata_.planes()[0].bytesused = buf.bytesused;\n> >  \t}\n> >  \n> >  \treturn buffer;\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 6E056BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 18:31:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B128A6916A;\n\tThu,  2 Sep 2021 20:31:30 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id ED3E360255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 20:31:29 +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 658AA45E;\n\tThu,  2 Sep 2021 20:31:29 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"igtw4dRA\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630607489;\n\tbh=ej+ugRxBcIqYJDSUeeBzA0Qm/bu615nG61IFpOijmuQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=igtw4dRAwHHsaK6UMNC2uJqOOP/POZGggnfco6SPcQOLXKKOL1VvZVe6siQMbbiXS\n\tsIztOw3UxaU3fpl7sZmFNMCHfqGuERrYxfqsb1x1pXXokaZG8n6o1nuHIizgQNMlhT\n\tPj8/TVIM9Am03e3BqtGfzysuoCEQK6fzmw4QcGkY=","Date":"Thu, 2 Sep 2021 21:31:13 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTEYccqJgkR/A4Id@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-12-laurent.pinchart@ideasonboard.com>\n\t<aad3bac8-de8a-f9a0-f590-0f9c26571dfb@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<aad3bac8-de8a-f9a0-f590-0f9c26571dfb@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 11/12] libcamera:\n\tv4l2_videodevice: Split planes when dequeuing buffer","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19309,"web_url":"https://patchwork.libcamera.org/comment/19309/","msgid":"<YTEZtNWvA059n8ty@pendragon.ideasonboard.com>","date":"2021-09-02T18:36:36","subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","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, Sep 02, 2021 at 10:23:48AM +0100, Kieran Bingham wrote:\n> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > V4L2 describes multi-planar formats with different 4CCs depending on\n> > whether or not the planes are stored contiguously in memory. Support\n> > this when translating between PixelFormat and V4L2PixelFormat.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/internal/formats.h |   2 +-\n> >  src/libcamera/formats.cpp            | 141 ++++++++++++++++-----------\n> >  src/libcamera/pipeline/ipu3/cio2.cpp |   2 +-\n> >  src/libcamera/v4l2_pixelformat.cpp   |  11 ++-\n> >  src/v4l2/v4l2_camera_proxy.cpp       |   6 +-\n> >  5 files changed, 99 insertions(+), 63 deletions(-)\n> > \n> > diff --git a/include/libcamera/internal/formats.h b/include/libcamera/internal/formats.h\n> > index 8f314e99889b..f9ce5f0834fa 100644\n> > --- a/include/libcamera/internal/formats.h\n> > +++ b/include/libcamera/internal/formats.h\n> > @@ -55,7 +55,7 @@ public:\n> >  \t/* \\todo Add support for non-contiguous memory planes */\n> >  \tconst char *name;\n> >  \tPixelFormat format;\n> > -\tV4L2PixelFormat v4l2Format;\n> > +\tstd::array<V4L2PixelFormat, 2> v4l2Format;\n> \n> As discussed below, I'm curious if a struct would be clearer here and\n> help prevent bugs later...\n> \n> \tstruct {\n> \t\tV4L2PixelFormat planar;\n> \t\tV4L2PixelFormat multiplanar;\n> \t} v4l2Format;\n> \n> >  \tunsigned int bitsPerPixel;\n> >  \tenum ColourEncoding colourEncoding;\n> >  \tbool packed;\n> > diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp\n> > index 76be93bc1c5c..ce7c5a2d267e 100644\n> > --- a/src/libcamera/formats.cpp\n> > +++ b/src/libcamera/formats.cpp\n> > @@ -56,7 +56,14 @@ LOG_DEFINE_CATEGORY(Formats)\n> >   * \\brief The PixelFormat described by this instance\n> >   *\n> >   * \\var PixelFormatInfo::v4l2Format\n> > - * \\brief The V4L2 pixel format corresponding to the PixelFormat\n> > + * \\brief The V4L2 pixel formats corresponding to the PixelFormat\n> > + *\n> > + * Multiple V4L2 formats may exist for one PixelFormat when the format uses\n> > + * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate\n> > + * planes formats. The two entries in the array store the contiguous and\n> > + * non-contiguous V4L2 formats respectively. If the PixelFormat isn't a\n> > + * multiplanar format, or if no corresponding non-contiguous V4L2 format\n> > + * exists, the second entry is invalid.\n> >   *\n> >   * \\var PixelFormatInfo::bitsPerPixel\n> >   * \\brief The average number of bits per pixel\n> > @@ -149,7 +156,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::RGB565, {\n> >  \t\t.name = \"RGB565\",\n> >  \t\t.format = formats::RGB565,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -159,7 +166,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::RGB565_BE, {\n> >  \t\t.name = \"RGB565_BE\",\n> >  \t\t.format = formats::RGB565_BE,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565X),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565X), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -169,7 +176,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::BGR888, {\n> >  \t\t.name = \"BGR888\",\n> >  \t\t.format = formats::BGR888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB24), },\n> >  \t\t.bitsPerPixel = 24,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -179,7 +186,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::RGB888, {\n> >  \t\t.name = \"RGB888\",\n> >  \t\t.format = formats::RGB888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGR24), },\n> >  \t\t.bitsPerPixel = 24,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -189,7 +196,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::XRGB8888, {\n> >  \t\t.name = \"XRGB8888\",\n> >  \t\t.format = formats::XRGB8888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XBGR32),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XBGR32), },\n> >  \t\t.bitsPerPixel = 32,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -199,7 +206,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::XBGR8888, {\n> >  \t\t.name = \"XBGR8888\",\n> >  \t\t.format = formats::XBGR8888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBX32),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBX32), },\n> >  \t\t.bitsPerPixel = 32,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -209,7 +216,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::BGRX8888, {\n> >  \t\t.name = \"BGRX8888\",\n> >  \t\t.format = formats::BGRX8888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XRGB32),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XRGB32), },\n> >  \t\t.bitsPerPixel = 32,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -219,7 +226,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::ABGR8888, {\n> >  \t\t.name = \"ABGR8888\",\n> >  \t\t.format = formats::ABGR8888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBA32), },\n> >  \t\t.bitsPerPixel = 32,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -229,7 +236,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::ARGB8888, {\n> >  \t\t.name = \"ARGB8888\",\n> >  \t\t.format = formats::ARGB8888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ABGR32), },\n> >  \t\t.bitsPerPixel = 32,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -239,7 +246,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::BGRA8888, {\n> >  \t\t.name = \"BGRA8888\",\n> >  \t\t.format = formats::BGRA8888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ARGB32), },\n> >  \t\t.bitsPerPixel = 32,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -249,7 +256,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::RGBA8888, {\n> >  \t\t.name = \"RGBA8888\",\n> >  \t\t.format = formats::RGBA8888,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGRA32), },\n> >  \t\t.bitsPerPixel = 32,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> >  \t\t.packed = false,\n> > @@ -261,7 +268,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::YUYV, {\n> >  \t\t.name = \"YUYV\",\n> >  \t\t.format = formats::YUYV,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YUYV), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -271,7 +278,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::YVYU, {\n> >  \t\t.name = \"YVYU\",\n> >  \t\t.format = formats::YVYU,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YVYU), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -281,7 +288,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::UYVY, {\n> >  \t\t.name = \"UYVY\",\n> >  \t\t.format = formats::UYVY,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_UYVY), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -291,7 +298,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::VYUY, {\n> >  \t\t.name = \"VYUY\",\n> >  \t\t.format = formats::VYUY,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_VYUY), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -303,7 +310,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::NV12, {\n> >  \t\t.name = \"NV12\",\n> >  \t\t.format = formats::NV12,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > +\t\t.v4l2Format = {\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV12M),\n> > +\t\t},\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -313,7 +323,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::NV21, {\n> >  \t\t.name = \"NV21\",\n> >  \t\t.format = formats::NV21,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > +\t\t.v4l2Format = {\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV21M),\n> > +\t\t},\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -323,7 +336,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::NV16, {\n> >  \t\t.name = \"NV16\",\n> >  \t\t.format = formats::NV16,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > +\t\t.v4l2Format = {\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV16M),\n> > +\t\t},\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -333,7 +349,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::NV61, {\n> >  \t\t.name = \"NV61\",\n> >  \t\t.format = formats::NV61,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > +\t\t.v4l2Format = {\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_NV61M),\n> > +\t\t},\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -343,7 +362,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::NV24, {\n> >  \t\t.name = \"NV24\",\n> >  \t\t.format = formats::NV24,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV24),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV24), },\n> >  \t\t.bitsPerPixel = 24,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -353,7 +372,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::NV42, {\n> >  \t\t.name = \"NV42\",\n> >  \t\t.format = formats::NV42,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV42),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV42), },\n> >  \t\t.bitsPerPixel = 24,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -363,7 +382,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::YUV420, {\n> >  \t\t.name = \"YUV420\",\n> >  \t\t.format = formats::YUV420,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > +\t\t.v4l2Format = {\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV420M),\n> > +\t\t},\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -373,7 +395,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::YVU420, {\n> >  \t\t.name = \"YVU420\",\n> >  \t\t.format = formats::YVU420,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > +\t\t.v4l2Format = {\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YVU420M),\n> > +\t\t},\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -383,7 +408,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::YUV422, {\n> >  \t\t.name = \"YUV422\",\n> >  \t\t.format = formats::YUV422,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > +\t\t.v4l2Format = {\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > +\t\t\tV4L2PixelFormat(V4L2_PIX_FMT_YUV422M),\n> > +\t\t},\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -395,7 +423,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::R8, {\n> >  \t\t.name = \"R8\",\n> >  \t\t.format = formats::R8,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_GREY),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_GREY), },\n> >  \t\t.bitsPerPixel = 8,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -407,7 +435,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SBGGR8, {\n> >  \t\t.name = \"SBGGR8\",\n> >  \t\t.format = formats::SBGGR8,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), },\n> >  \t\t.bitsPerPixel = 8,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -417,7 +445,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGBRG8, {\n> >  \t\t.name = \"SGBRG8\",\n> >  \t\t.format = formats::SGBRG8,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), },\n> >  \t\t.bitsPerPixel = 8,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -427,7 +455,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGRBG8, {\n> >  \t\t.name = \"SGRBG8\",\n> >  \t\t.format = formats::SGRBG8,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), },\n> >  \t\t.bitsPerPixel = 8,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -437,7 +465,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SRGGB8, {\n> >  \t\t.name = \"SRGGB8\",\n> >  \t\t.format = formats::SRGGB8,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), },\n> >  \t\t.bitsPerPixel = 8,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -447,7 +475,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SBGGR10, {\n> >  \t\t.name = \"SBGGR10\",\n> >  \t\t.format = formats::SBGGR10,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -457,7 +485,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGBRG10, {\n> >  \t\t.name = \"SGBRG10\",\n> >  \t\t.format = formats::SGBRG10,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -467,7 +495,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGRBG10, {\n> >  \t\t.name = \"SGRBG10\",\n> >  \t\t.format = formats::SGRBG10,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -477,7 +505,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SRGGB10, {\n> >  \t\t.name = \"SRGGB10\",\n> >  \t\t.format = formats::SRGGB10,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -487,7 +515,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SBGGR10_CSI2P, {\n> >  \t\t.name = \"SBGGR10_CSI2P\",\n> >  \t\t.format = formats::SBGGR10_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -497,7 +525,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGBRG10_CSI2P, {\n> >  \t\t.name = \"SGBRG10_CSI2P\",\n> >  \t\t.format = formats::SGBRG10_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -507,7 +535,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGRBG10_CSI2P, {\n> >  \t\t.name = \"SGRBG10_CSI2P\",\n> >  \t\t.format = formats::SGRBG10_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -517,7 +545,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SRGGB10_CSI2P, {\n> >  \t\t.name = \"SRGGB10_CSI2P\",\n> >  \t\t.format = formats::SRGGB10_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -527,7 +555,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SBGGR12, {\n> >  \t\t.name = \"SBGGR12\",\n> >  \t\t.format = formats::SBGGR12,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -537,7 +565,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGBRG12, {\n> >  \t\t.name = \"SGBRG12\",\n> >  \t\t.format = formats::SGBRG12,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -547,7 +575,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGRBG12, {\n> >  \t\t.name = \"SGRBG12\",\n> >  \t\t.format = formats::SGRBG12,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -557,7 +585,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SRGGB12, {\n> >  \t\t.name = \"SRGGB12\",\n> >  \t\t.format = formats::SRGGB12,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -567,7 +595,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SBGGR12_CSI2P, {\n> >  \t\t.name = \"SBGGR12_CSI2P\",\n> >  \t\t.format = formats::SBGGR12_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -577,7 +605,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGBRG12_CSI2P, {\n> >  \t\t.name = \"SGBRG12_CSI2P\",\n> >  \t\t.format = formats::SGBRG12_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -587,7 +615,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGRBG12_CSI2P, {\n> >  \t\t.name = \"SGRBG12_CSI2P\",\n> >  \t\t.format = formats::SGRBG12_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -597,7 +625,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SRGGB12_CSI2P, {\n> >  \t\t.name = \"SRGGB12_CSI2P\",\n> >  \t\t.format = formats::SRGGB12_CSI2P,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), },\n> >  \t\t.bitsPerPixel = 12,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -607,7 +635,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SBGGR16, {\n> >  \t\t.name = \"SBGGR16\",\n> >  \t\t.format = formats::SBGGR16,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -617,7 +645,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGBRG16, {\n> >  \t\t.name = \"SGBRG16\",\n> >  \t\t.format = formats::SGBRG16,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -627,7 +655,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGRBG16, {\n> >  \t\t.name = \"SGRBG16\",\n> >  \t\t.format = formats::SGRBG16,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -637,7 +665,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SRGGB16, {\n> >  \t\t.name = \"SRGGB16\",\n> >  \t\t.format = formats::SRGGB16,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), },\n> >  \t\t.bitsPerPixel = 16,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = false,\n> > @@ -647,7 +675,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SBGGR10_IPU3, {\n> >  \t\t.name = \"SBGGR10_IPU3\",\n> >  \t\t.format = formats::SBGGR10_IPU3,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -658,7 +686,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGBRG10_IPU3, {\n> >  \t\t.name = \"SGBRG10_IPU3\",\n> >  \t\t.format = formats::SGBRG10_IPU3,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -668,7 +696,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SGRBG10_IPU3, {\n> >  \t\t.name = \"SGRBG10_IPU3\",\n> >  \t\t.format = formats::SGRBG10_IPU3,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -678,7 +706,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::SRGGB10_IPU3, {\n> >  \t\t.name = \"SRGGB10_IPU3\",\n> >  \t\t.format = formats::SRGGB10_IPU3,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10), },\n> >  \t\t.bitsPerPixel = 10,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> >  \t\t.packed = true,\n> > @@ -690,7 +718,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> >  \t{ formats::MJPEG, {\n> >  \t\t.name = \"MJPEG\",\n> >  \t\t.format = formats::MJPEG,\n> > -\t\t.v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),\n> > +\t\t.v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_MJPEG), },\n> >  \t\t.bitsPerPixel = 0,\n> >  \t\t.colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> >  \t\t.packed = false,\n> > @@ -736,7 +764,8 @@ const PixelFormatInfo &PixelFormatInfo::info(const V4L2PixelFormat &format)\n> >  {\n> >  \tconst auto &info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),\n> >  \t\t\t\t\t[format](auto pair) {\n> > -\t\t\t\t\t\treturn pair.second.v4l2Format == format;\n> > +\t\t\t\t\t\treturn pair.second.v4l2Format[0] == format ||\n> > +\t\t\t\t\t\t       pair.second.v4l2Format[1] == format;\n> >  \t\t\t\t\t});\n> >  \tif (info == pixelFormatInfo.end())\n> >  \t\treturn pixelFormatInfoInvalid;\n> > diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > index 9cedcb5b2879..5a9cffc80c8d 100644\n> > --- a/src/libcamera/pipeline/ipu3/cio2.cpp\n> > +++ b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > @@ -205,7 +205,7 @@ int CIO2Device::configure(const Size &size, V4L2DeviceFormat *outputFormat)\n> >  \n> >  \tconst PixelFormatInfo &info = PixelFormatInfo::info(itInfo->second);\n> >  \n> > -\toutputFormat->fourcc = info.v4l2Format;\n> > +\toutputFormat->fourcc = info.v4l2Format[0];\n> >  \toutputFormat->size = sensorFormat.size;\n> >  \toutputFormat->planesCount = 1;\n> >  \n> > diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp\n> > index 93fc4446cc64..93ead8928ed7 100644\n> > --- a/src/libcamera/v4l2_pixelformat.cpp\n> > +++ b/src/libcamera/v4l2_pixelformat.cpp\n> > @@ -67,12 +67,19 @@ const std::map<V4L2PixelFormat, PixelFormat> vpf2pf{\n> >  \n> >  \t/* YUV planar formats. */\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV16), formats::NV16 },\n> > +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV16M), formats::NV16 },\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV61), formats::NV61 },\n> > +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV61M), formats::NV61 },\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV12), formats::NV12 },\n> > +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV12M), formats::NV12 },\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_NV21), formats::NV21 },\n> > +\t{ V4L2PixelFormat(V4L2_PIX_FMT_NV21M), formats::NV21 },\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV420), formats::YUV420 },\n> > +\t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV420M), formats::YUV420 },\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_YVU420), formats::YVU420 },\n> > +\t{ V4L2PixelFormat(V4L2_PIX_FMT_YVU420M), formats::YVU420 },\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV422P), formats::YUV422 },\n> > +\t{ V4L2PixelFormat(V4L2_PIX_FMT_YUV422M), formats::YUV422 },\n> \n> I'm worried that we're losing information here.\n> But presumably it's handled by whether we use mplane or not on the\n> device...?\n\nWe don't expose to applications whether the camera requires contiguous\nbuffers or can support non-contiguous buffers. We don't otherwise lose\ninformation, as we pick a specific V4L2 format from the PixelFormat,\nbased on an explicit selection of multiplanar or singleplanar V4L2\nformat by the pipeline handler.\n\n> >  \t/* Greyscale formats. */\n> >  \t{ V4L2PixelFormat(V4L2_PIX_FMT_GREY), formats::R8 },\n> > @@ -202,13 +209,13 @@ PixelFormat V4L2PixelFormat::toPixelFormat() const\n> >   * \\return The V4L2PixelFormat corresponding to \\a pixelFormat\n> >   */\n> >  V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat,\n> > -\t\t\t\t\t\t [[maybe_unused]] bool multiplanar)\n> > +\t\t\t\t\t\t bool multiplanar)\n> >  {\n> >  \tconst PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);\n> >  \tif (!info.isValid())\n> >  \t\treturn V4L2PixelFormat();\n> >  \n> > -\treturn info.v4l2Format;\n> > +\treturn info.v4l2Format[multiplanar ? 1 : 0];\n> \n> I was going to say, a struct with named fields of 'planar', and\n> 'multiplanar' might be a better, though I like the simplicity of this bit.\n\nWould be easy to rewrite though.\n\n> >  }\n> >  \n> >  } /* namespace libcamera */\n> > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> > index 7682c4bddf90..8d8ee395954f 100644\n> > --- a/src/v4l2/v4l2_camera_proxy.cpp\n> > +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> > @@ -169,7 +169,7 @@ void V4L2CameraProxy::setFmtFromConfig(const StreamConfiguration &streamConfig)\n> >  \n> >  \tv4l2PixFormat_.width        = size.width;\n> >  \tv4l2PixFormat_.height       = size.height;\n> > -\tv4l2PixFormat_.pixelformat  = info.v4l2Format;\n> > +\tv4l2PixFormat_.pixelformat  = info.v4l2Format[0];\n> >  \tv4l2PixFormat_.field        = V4L2_FIELD_NONE;\n> >  \tv4l2PixFormat_.bytesperline = streamConfig.stride;\n> >  \tv4l2PixFormat_.sizeimage    = streamConfig.frameSize;\n> > @@ -276,7 +276,7 @@ int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *file, struct v4l2_fmtdesc *\n> >  \t/* \\todo Add map from format to description. */\n> >  \tutils::strlcpy(reinterpret_cast<char *>(arg->description),\n> >  \t\t       \"Video Format Description\", sizeof(arg->description));\n> > -\targ->pixelformat = PixelFormatInfo::info(format).v4l2Format;\n> > +\targ->pixelformat = PixelFormatInfo::info(format).v4l2Format[0];\n> >  \n> >  \tmemset(arg->reserved, 0, sizeof(arg->reserved));\n> >  \n> > @@ -315,7 +315,7 @@ int V4L2CameraProxy::tryFormat(struct v4l2_format *arg)\n> >  \n> >  \targ->fmt.pix.width        = config.size.width;\n> >  \targ->fmt.pix.height       = config.size.height;\n> > -\targ->fmt.pix.pixelformat  = info.v4l2Format;\n> > +\targ->fmt.pix.pixelformat  = info.v4l2Format[0];\n> \n> But for occasions like here, and in the CIO2\n> \targ->fmt.pix.pixelformat  = info.v4l2Format.planar;\n> \n> Would be far more descriptive over which one is being chosen, and might\n> help make it easier to spot issues when debugging multiplanar format\n> bugs ...\n\nI agree. I don't like the names \"planar\" and \"multiplanar\" though, as\nthat's a bit ambiguous. V4L2 did a really bad job when it comes to\nnaming here. I'll try to think of better names after sleeping over it,\nbut please feel free to suggest alternatives :-) Bonus points of they're\nshort and have the same length :-)\n\n> >  \targ->fmt.pix.field        = V4L2_FIELD_NONE;\n> >  \targ->fmt.pix.bytesperline = config.stride;\n> >  \targ->fmt.pix.sizeimage    = config.frameSize;\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 D0058BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 Sep 2021 18:36:55 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 33EFC6916D;\n\tThu,  2 Sep 2021 20:36:55 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6B1C460255\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 Sep 2021 20:36:53 +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 CEA9145E;\n\tThu,  2 Sep 2021 20:36:52 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"e0pgiQJx\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630607813;\n\tbh=CsSqv2bD291hoQDrNHnGGh/PwkWs+3RHCe90OUIfOhA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=e0pgiQJxixzCRw68zD67tbtP1PoO8AOotvDTkgInLOqLnNCmyphv5HCEGe/iajTuE\n\teDWRlDKnxDgFtulmJh8EzYYxsIfc0MqgLh75QL1NtSZwebadWh4jGgrRa/10c/2cIs\n\taNoxwBCCOPzml+UG0syDlVAVOYmlJIybAzcAVMko=","Date":"Thu, 2 Sep 2021 21:36:36 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTEZtNWvA059n8ty@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-5-laurent.pinchart@ideasonboard.com>\n\t<797f3608-e856-3094-5e01-4c71c19cee0e@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<797f3608-e856-3094-5e01-4c71c19cee0e@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19319,"web_url":"https://patchwork.libcamera.org/comment/19319/","msgid":"<ed14c57f-8b16-f2a0-a13f-fcfd4069897b@ideasonboard.com>","date":"2021-09-03T08:24:02","subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 19:07, Laurent Pinchart wrote:\n> Hi Kieran,\n> \n> On Thu, Sep 02, 2021 at 10:43:56AM +0100, Kieran Bingham wrote:\n>> On 02/09/2021 05:22, Laurent Pinchart wrote:\n>>> Multi-planar frame buffers can store their planes contiguously in\n>>> memory, or split them in discontiguous memory areas. Add a private\n>>> function to check in which of these two categories the frame buffer\n>>> belongs. This will be used to correctly handle the differences between\n>>> the V4L2 single and multi planar APIs.\n>>>\n>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>>> ---\n>>>  include/libcamera/internal/framebuffer.h |  2 ++\n>>>  src/libcamera/framebuffer.cpp            | 45 +++++++++++++++++++++++-\n>>>  2 files changed, 46 insertions(+), 1 deletion(-)\n>>>\n>>> diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h\n>>> index 606aed2b4782..cd33c295466e 100644\n>>> --- a/include/libcamera/internal/framebuffer.h\n>>> +++ b/include/libcamera/internal/framebuffer.h\n>>> @@ -21,9 +21,11 @@ public:\n>>>  \tPrivate();\n>>>  \n>>>  \tvoid setRequest(Request *request) { request_ = request; }\n>>> +\tbool isContiguous() const { return isContiguous_; }\n>>>  \n>>>  private:\n>>>  \tRequest *request_;\n>>> +\tbool isContiguous_;\n>>>  };\n>>>  \n>>>  } /* namespace libcamera */\n>>> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n>>> index 53ef89bf458f..99265b44da43 100644\n>>> --- a/src/libcamera/framebuffer.cpp\n>>> +++ b/src/libcamera/framebuffer.cpp\n>>> @@ -106,7 +106,7 @@ LOG_DEFINE_CATEGORY(Buffer)\n>>>   */\n>>>  \n>>>  FrameBuffer::Private::Private()\n>>> -\t: request_(nullptr)\n>>> +\t: request_(nullptr), isContiguous_(true)\n>>>  {\n>>>  }\n>>>  \n>>> @@ -120,6 +120,17 @@ FrameBuffer::Private::Private()\n>>>   * handlers, it is called by the pipeline handlers themselves.\n>>>   */\n>>>  \n>>> +/**\n>>> + * \\fn FrameBuffer::Private::isContiguous()\n>>> + * \\brief Check if the frame buffer stores planes contiguously in memory\n>>> + *\n>>> + * Multi-planar frame buffers can store their planes contiguously in memory, or\n>>> + * split them in discontiguous memory areas. This function checks in which of\n>>\n>> 'split them into discontiguous'\n>>\n>>> + * these two categories the frame buffer belongs.\n>>> + *\n>>> + * \\return True if the planes are stored contiguously in memory, false otherwise\n>>> + */\n>>> +\n>>>  /**\n>>>   * \\class FrameBuffer\n>>>   * \\brief Frame buffer data and its associated dynamic metadata\n>>> @@ -199,9 +210,41 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n>>>  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n>>>  \t  cookie_(cookie)\n>>>  {\n>>> +\tunsigned int offset = 0;\n>>> +\n>>>  \t/* \\todo Remove the assertions after sufficient testing */\n>>>  \tfor (const auto &plane : planes_)\n>>>  \t\tASSERT(plane.offset != Plane::kInvalidOffset);\n>>> +\n>>> +\tbool isContiguous = true;\n>>> +\tino_t inode = 0;\n>>> +\n>>> +\tfor (const auto &plane : planes_) {\n>>> +\t\tif (plane.offset != offset) {\n>>> +\t\t\tisContiguous = false;\n>>> +\t\t\tbreak;\n>>> +\t\t}\n>>> +\n>>> +\t\t/*\n>>> +\t\t * Two different dmabuf file descriptors may still refer to the\n>>> +\t\t * same dmabuf instance. Check this using inodes.\n>>\n>> Going back to the FileDescriptor::inode() extension, if that cached the\n>> inode (it can't change) on either first call, or construction, couldn't\n>> this whole check be simplified to:\n>>\n>> \t\tif (plane.fd.inode() != planes_[0].fd.inode()) {\n>> \t\t\tisContiguous = false;\n>> \t\t\tbreak;\n>> \t\t}\n> \n> This would call fstat() every time, while the fd comparison here is\n> meant to skip the fstat() calls in case the numerical fd match. We could\n> do\n\nDid you miss the part where I said \"If we cache the inode in\nFileDescriptor\"?\n\nMy intention was that caching there would mean we can simplify\ncomparisons here, without incurring repeated fstat calls.\n\n\n> \t\tif (plane.fd != planes_[0].fd)\n> \n> if we introduced an operator==(), but as explained in a reply to the\n> patch that adds inode(), I'd like to handle that later, to avoid\n> blocking this series.\n\nOk, I still don't see the harm in caching the inode within\nFileDescriptor(), it can't change once read the first time.\n\nBut, it can be deferred:\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n\n>>> +\t\t */\n>>> +\t\tif (plane.fd.fd() != planes_[0].fd.fd()) {\n>>> +\t\t\tif (!inode)\n>>> +\t\t\t\tinode = planes_[0].fd.inode();\n>>> +\t\t\tif (plane.fd.inode() != inode) {\n>>> +\t\t\t\tisContiguous = false;\n>>> +\t\t\t\tbreak;\n>>> +\t\t\t}\n>>> +\t\t}\n>>> +\n>>> +\t\toffset += plane.length;\n>>> +\t}\n>>> +\n>>> +\tLOG(Buffer, Debug)\n>>> +\t\t<< \"Buffer is \" << (isContiguous ? \"not \" : \"\") << \"contiguous\";\n>>> +\n>>> +\t_d()->isContiguous_ = isContiguous;\n>>>  }\n>>>  \n>>>  /**\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 6A95ABDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 08:24:08 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C0AA36916D;\n\tFri,  3 Sep 2021 10:24:07 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9D91D60251\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 10:24:05 +0200 (CEST)","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 14F18BBE;\n\tFri,  3 Sep 2021 10:24:05 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"V+HQV2kp\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630657445;\n\tbh=+TQ0gGpJ/+GDSfqAvV9chwwDNawgaexvKkRXGQgTzXs=;\n\th=From:Subject:To:Cc:References:Date:In-Reply-To:From;\n\tb=V+HQV2kpYlMoF8yZTqOnjh9atjT6qNgEtYn7QoUs9MR3VNXMjq5OpCJsNUaC2TSgC\n\t7C0jiUf22Hy3cpXYbjOLGohGMtTRsow156joVYLcILtaduQY2ZyACfjE981r27+KjT\n\tU/6tVy+aN4KAcZLWR6KG0TiCiIvww6xIQGw1kuro=","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>\n\t<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>\n\t<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>","Message-ID":"<ed14c57f-8b16-f2a0-a13f-fcfd4069897b@ideasonboard.com>","Date":"Fri, 3 Sep 2021 09:24:02 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19320,"web_url":"https://patchwork.libcamera.org/comment/19320/","msgid":"<fc5e4c45-564b-a1c7-9f87-beafd1f9749a@ideasonboard.com>","date":"2021-09-03T08:26:40","subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 19:20, Laurent Pinchart wrote:\n> Hi Kieran,\n> \n> On Thu, Sep 02, 2021 at 11:03:47AM +0100, Kieran Bingham wrote:\n>> On 02/09/2021 05:22, Laurent Pinchart wrote:\n>>> The number of metadata planes should always match the number of frame\n>>> buffer planes. Enforce this by making the vector private and providing\n>>> accessor functions.\n>>>\n>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>>> ---\n>>>  include/libcamera/framebuffer.h    | 10 +++++++++-\n>>>  src/cam/camera_session.cpp         |  4 ++--\n>>>  src/cam/file_sink.cpp              |  2 +-\n>>>  src/libcamera/framebuffer.cpp      | 12 +++++++++---\n>>>  src/libcamera/v4l2_videodevice.cpp | 14 +++++++-------\n>>>  src/qcam/main_window.cpp           |  2 +-\n>>>  src/qcam/viewfinder_gl.cpp         |  2 +-\n>>>  src/qcam/viewfinder_qt.cpp         |  2 +-\n>>>  src/v4l2/v4l2_camera_proxy.cpp     |  2 +-\n>>>  9 files changed, 32 insertions(+), 18 deletions(-)\n>>>\n>>> diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n>>> index fd68ed0a139d..7f2f176af691 100644\n>>> --- a/include/libcamera/framebuffer.h\n>>> +++ b/include/libcamera/framebuffer.h\n>>> @@ -13,6 +13,7 @@\n>>>  #include <vector>\n>>>  \n>>>  #include <libcamera/base/class.h>\n>>> +#include <libcamera/base/span.h>\n>>>  \n>>>  #include <libcamera/file_descriptor.h>\n>>>  \n>>> @@ -34,7 +35,14 @@ struct FrameMetadata {\n>>>  \tStatus status;\n>>>  \tunsigned int sequence;\n>>>  \tuint64_t timestamp;\n>>> -\tstd::vector<Plane> planes;\n>>> +\n>>> +\tSpan<Plane> planes() { return planes_; }\n>>> +\tSpan<const Plane> planes() const { return planes_; }\n>>\n>> Returning a span here is nice.\n>>\n>> This likely causes compile breakage for any external app.\n>>\n>> I know we're not ABI/API stable, but I wonder if we should highlight\n>> when we do cause breakage somehow, perhaps in the commit message at\n>> least, as we already have external users who we might want to notify.\n> \n> Most changes to public header files will be ABI/API breakages at this\n> point, and users will certainly notice when they get a compilation\n> failure :-) What value do you think this would bring, who would read\n> those messages ? I think it could be different for things that change\n> the ABI without breaking compilation and that would require more than a\n> recompilation to fix, there a warning seems to have more value, but I'm\n> also sure we'll forget from time to time, if not most of the time.\n\nBecause I think we need to start being more aware of when we introduce\nboth ABI and API breakages.\n\nAt least making it explicitly noted in the commit message states that we\nwere aware of the breakage at that point.\n\n\n>>> +\n>>> +private:\n>>> +\tfriend class FrameBuffer;\n>>> +\n>>> +\tstd::vector<Plane> planes_;\n>>>  };\n>>>  \n>>>  class FrameBuffer final : public Extensible\n>>> diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\n>>> index 60d640f2b15c..32a373a99b72 100644\n>>> --- a/src/cam/camera_session.cpp\n>>> +++ b/src/cam/camera_session.cpp\n>>> @@ -374,9 +374,9 @@ void CameraSession::processRequest(Request *request)\n>>>  \t\t     << \" bytesused: \";\n>>>  \n>>>  \t\tunsigned int nplane = 0;\n>>> -\t\tfor (const FrameMetadata::Plane &plane : metadata.planes) {\n>>> +\t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n>>>  \t\t\tinfo << plane.bytesused;\n>>> -\t\t\tif (++nplane < metadata.planes.size())\n>>> +\t\t\tif (++nplane < metadata.planes().size())\n>>>  \t\t\t\tinfo << \"/\";\n>>>  \t\t}\n>>>  \t}\n>>> diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\n>>> index 0b529e3eb767..0fc7d621f50b 100644\n>>> --- a/src/cam/file_sink.cpp\n>>> +++ b/src/cam/file_sink.cpp\n>>> @@ -110,7 +110,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)\n>>>  \n>>>  \tfor (unsigned int i = 0; i < buffer->planes().size(); ++i) {\n>>>  \t\tconst FrameBuffer::Plane &plane = buffer->planes()[i];\n>>> -\t\tconst FrameMetadata::Plane &meta = buffer->metadata().planes[i];\n>>> +\t\tconst FrameMetadata::Plane &meta = buffer->metadata().planes()[i];\n>>>  \n>>>  \t\tuint8_t *data = planeData_[&plane];\n>>>  \t\tunsigned int length = std::min(meta.bytesused, plane.length);\n>>> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n>>> index c1529dfb0ad1..83cf7b83d182 100644\n>>> --- a/src/libcamera/framebuffer.cpp\n>>> +++ b/src/libcamera/framebuffer.cpp\n>>> @@ -91,8 +91,14 @@ LOG_DEFINE_CATEGORY(Buffer)\n>>>   */\n>>>  \n>>>  /**\n>>> - * \\var FrameMetadata::planes\n>>> - * \\brief Array of per-plane metadata\n>>> + * \\fn FrameMetadata::planes()\n>>> + * \\copydoc FrameMetadata::planes() const\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\fn FrameMetadata::planes() const\n>>> + * \\brief Retrieve the array of per-plane metadata\n>>> + * \\return The array of per-plane metadata\n>>>   */\n>>>  \n>>>  /**\n>>> @@ -210,7 +216,7 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n>>>  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n>>>  \t  cookie_(cookie)\n>>>  {\n>>> -\tmetadata_.planes.resize(planes_.size());\n>>> +\tmetadata_.planes_.resize(planes_.size());\n>>>  \n>>>  \tunsigned int offset = 0;\n>>>  \n>>> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n>>> index a51971879e75..82ddaed3656f 100644\n>>> --- a/src/libcamera/v4l2_videodevice.cpp\n>>> +++ b/src/libcamera/v4l2_videodevice.cpp\n>>> @@ -1485,14 +1485,14 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n>>>  \n>>>  \t\tif (multiPlanar) {\n>>>  \t\t\tunsigned int nplane = 0;\n>>> -\t\t\tfor (const FrameMetadata::Plane &plane : metadata.planes) {\n>>> +\t\t\tfor (const FrameMetadata::Plane &plane : metadata.planes()) {\n>>>  \t\t\t\tv4l2Planes[nplane].bytesused = plane.bytesused;\n>>>  \t\t\t\tv4l2Planes[nplane].length = buffer->planes()[nplane].length;\n>>>  \t\t\t\tnplane++;\n>>>  \t\t\t}\n>>>  \t\t} else {\n>>> -\t\t\tif (metadata.planes.size())\n>>> -\t\t\t\tbuf.bytesused = metadata.planes[0].bytesused;\n>>> +\t\t\tif (metadata.planes().size())\n>>> +\t\t\t\tbuf.bytesused = metadata.planes()[0].bytesused;\n>>>  \t\t}\n>>>  \n>>>  \t\tbuf.sequence = metadata.sequence;\n>>> @@ -1587,17 +1587,17 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n>>>  \t\t\t\t    + buf.timestamp.tv_usec * 1000ULL;\n>>>  \n>>>  \tif (multiPlanar) {\n>>> -\t\tif (buf.length > buffer->metadata_.planes.size()) {\n>>> +\t\tif (buf.length > buffer->metadata_.planes().size()) {\n>>>  \t\t\tLOG(V4L2, Error)\n>>>  \t\t\t\t<< \"Invalid number of planes (\" << buf.length\n>>> -\t\t\t\t<< \" != \" << buffer->metadata_.planes.size() << \")\";\n>>> +\t\t\t\t<< \" != \" << buffer->metadata_.planes().size() << \")\";\n>>>  \t\t\treturn nullptr;\n>>>  \t\t}\n>>>  \n>>>  \t\tfor (unsigned int nplane = 0; nplane < buf.length; nplane++)\n>>> -\t\t\tbuffer->metadata_.planes[nplane].bytesused = planes[nplane].bytesused;\n>>> +\t\t\tbuffer->metadata_.planes()[nplane].bytesused = planes[nplane].bytesused;\n>>>  \t} else {\n>>> -\t\tbuffer->metadata_.planes[0].bytesused = buf.bytesused;\n>>> +\t\tbuffer->metadata_.planes()[0].bytesused = buf.bytesused;\n>>>  \t}\n>>>  \n>>>  \treturn buffer;\n>>> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n>>> index 1536b2b5bd66..ac853e360aea 100644\n>>> --- a/src/qcam/main_window.cpp\n>>> +++ b/src/qcam/main_window.cpp\n>>> @@ -756,7 +756,7 @@ void MainWindow::processViewfinder(FrameBuffer *buffer)\n>>>  \n>>>  \tqDebug().noquote()\n>>>  \t\t<< QString(\"seq: %1\").arg(metadata.sequence, 6, 10, QLatin1Char('0'))\n>>> -\t\t<< \"bytesused:\" << metadata.planes[0].bytesused\n>>> +\t\t<< \"bytesused:\" << metadata.planes()[0].bytesused\n>>>  \t\t<< \"timestamp:\" << metadata.timestamp\n>>>  \t\t<< \"fps:\" << Qt::fixed << qSetRealNumberPrecision(2) << fps;\n>>>  \n>>> diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n>>> index 40226601f9fd..d2ef036974f4 100644\n>>> --- a/src/qcam/viewfinder_gl.cpp\n>>> +++ b/src/qcam/viewfinder_gl.cpp\n>>> @@ -125,7 +125,7 @@ void ViewFinderGL::render(libcamera::FrameBuffer *buffer,\n>>>  \t/*\n>>>  \t * \\todo Get the stride from the buffer instead of computing it naively\n>>>  \t */\n>>> -\tstride_ = buffer->metadata().planes[0].bytesused / size_.height();\n>>> +\tstride_ = buffer->metadata().planes()[0].bytesused / size_.height();\n>>\n>> Can this be obtained from the PixelFormatInfo now ?\n>> or do we still not expose that to applications...\n> \n> PixelFormatInfo is still internal. I'm considering exposing it, but then\n> it would be good to move the V4L2-specific part somewhere else.\n> \n>> Anyway, that's likely a change on top even if we could to solve the \\todo.\n>>\n>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>>\n>>>  \tupdate();\n>>>  \tbuffer_ = buffer;\n>>>  }\n>>> diff --git a/src/qcam/viewfinder_qt.cpp b/src/qcam/viewfinder_qt.cpp\n>>> index efa1d412584b..a0bf99b0b522 100644\n>>> --- a/src/qcam/viewfinder_qt.cpp\n>>> +++ b/src/qcam/viewfinder_qt.cpp\n>>> @@ -87,7 +87,7 @@ void ViewFinderQt::render(libcamera::FrameBuffer *buffer,\n>>>  \t}\n>>>  \n>>>  \tunsigned char *memory = mem.data();\n>>> -\tsize_t size = buffer->metadata().planes[0].bytesused;\n>>> +\tsize_t size = buffer->metadata().planes()[0].bytesused;\n>>>  \n>>>  \t{\n>>>  \t\tQMutexLocker locker(&mutex_);\n>>> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n>>> index 8d8ee395954f..68e47ee81834 100644\n>>> --- a/src/v4l2/v4l2_camera_proxy.cpp\n>>> +++ b/src/v4l2/v4l2_camera_proxy.cpp\n>>> @@ -212,7 +212,7 @@ void V4L2CameraProxy::updateBuffers()\n>>>  \n>>>  \t\tswitch (fmd.status) {\n>>>  \t\tcase FrameMetadata::FrameSuccess:\n>>> -\t\t\tbuf.bytesused = fmd.planes[0].bytesused;\n>>> +\t\t\tbuf.bytesused = fmd.planes()[0].bytesused;\n>>>  \t\t\tbuf.field = V4L2_FIELD_NONE;\n>>>  \t\t\tbuf.timestamp.tv_sec = fmd.timestamp / 1000000000;\n>>>  \t\t\tbuf.timestamp.tv_usec = fmd.timestamp % 1000000;\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 8162CBDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 08:26:46 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EE9566916A;\n\tFri,  3 Sep 2021 10:26:45 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3459360251\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 10:26:44 +0200 (CEST)","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 BCAC9BBE;\n\tFri,  3 Sep 2021 10:26:43 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"bUencieY\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630657603;\n\tbh=uMlJ5RwZE438cQCdIukWG+LCHZJ8NUM/c6llmCJkF+U=;\n\th=From:Subject:To:Cc:References:Date:In-Reply-To:From;\n\tb=bUencieYwvAQ7oRae8pXsAmKVXneuj69BGnRv+I4Y4g+FwPEP546Vh86MeyVqrjXq\n\tHxMl7smHPA5TCAG7QcGJOjDuihkhyWHkCXIk2DW/hBFcNZOAk3adjdG9xtQSRtaMDX\n\tYbJbaTIRbEsi0pKSLVypeo6OaF4voSderq6xwO3s=","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-9-laurent.pinchart@ideasonboard.com>\n\t<23fb7400-0ebd-276b-52f9-863442ebf4b1@ideasonboard.com>\n\t<YTEV40OWld/Oxe4J@pendragon.ideasonboard.com>","Message-ID":"<fc5e4c45-564b-a1c7-9f87-beafd1f9749a@ideasonboard.com>","Date":"Fri, 3 Sep 2021 09:26:40 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<YTEV40OWld/Oxe4J@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19322,"web_url":"https://patchwork.libcamera.org/comment/19322/","msgid":"<9791e06e-b8fc-bae9-fb9c-b39f195277a4@ideasonboard.com>","date":"2021-09-03T08:29:40","subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 02/09/2021 19:26, Laurent Pinchart wrote:\n> Hi Kieran,\n> \n> On Thu, Sep 02, 2021 at 11:21:29AM +0100, Kieran Bingham wrote:\n>> On 02/09/2021 05:23, Laurent Pinchart wrote:\n>>> Cache the PixelFormatInfo instead of looking it up in every call to\n>>> createBuffer(). This prepare for usage of the info in queueBuffer(), to\n>>\n>> s/prepare/prepares/\n>>\n>>> avoid a looking every time a buffer is queued.\n>>>\n>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>>> ---\n>>>  include/libcamera/internal/v4l2_videodevice.h |  1 +\n>>>  src/libcamera/v4l2_videodevice.cpp            | 19 +++++++++++--------\n>>>  2 files changed, 12 insertions(+), 8 deletions(-)\n>>>\n>>> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h\n>>> index 7a145f608a5b..6096cd609b97 100644\n>>> --- a/include/libcamera/internal/v4l2_videodevice.h\n>>> +++ b/include/libcamera/internal/v4l2_videodevice.h\n>>> @@ -243,6 +243,7 @@ private:\n>>>  \n>>>  \tV4L2Capability caps_;\n>>>  \tV4L2DeviceFormat format_;\n>>> +\tconst PixelFormatInfo *formatInfo_;\n>>>  \n>>>  \tenum v4l2_buf_type bufferType_;\n>>>  \tenum v4l2_memory memoryType_;\n>>> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n>>> index 82ddaed3656f..2d9a94c3c974 100644\n>>> --- a/src/libcamera/v4l2_videodevice.cpp\n>>> +++ b/src/libcamera/v4l2_videodevice.cpp\n>>> @@ -482,8 +482,8 @@ const std::string V4L2DeviceFormat::toString() const\n>>>   * \\param[in] deviceNode The file-system path to the video device node\n>>>   */\n>>>  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n>>> -\t: V4L2Device(deviceNode), cache_(nullptr), fdBufferNotifier_(nullptr),\n>>> -\t  streaming_(false)\n>>> +\t: V4L2Device(deviceNode), formatInfo_(nullptr), cache_(nullptr),\n>>> +\t  fdBufferNotifier_(nullptr), streaming_(false)\n>>>  {\n>>>  \t/*\n>>>  \t * We default to an MMAP based CAPTURE video device, however this will\n>>> @@ -586,6 +586,7 @@ int V4L2VideoDevice::open()\n>>>  \t\treturn ret;\n>>>  \t}\n>>>  \n>>> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n>>\n>> I shudder a little seeing code hugging the return like that ;-)\n>>\n>> But it's purely subjective, same below of course.\n> \n> We have various patterns indeed :-) I haven't been able to establish yet\n> what my mental process to deal with these are, sometimes it bothers me\n> too, but not always.\n> \n>>>  \treturn 0;\n>>>  }\n>>>  \n>>> @@ -681,6 +682,7 @@ int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)\n>>>  \t\treturn ret;\n>>>  \t}\n>>>  \n>>> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n>>>  \treturn 0;\n>>>  }\n>>>  \n>>> @@ -695,6 +697,8 @@ void V4L2VideoDevice::close()\n>>>  \treleaseBuffers();\n>>>  \tdelete fdBufferNotifier_;\n>>>  \n>>> +\tformatInfo_ = nullptr;\n>>> +\n>>>  \tV4L2Device::close();\n>>>  }\n>>>  \n>>> @@ -787,6 +791,7 @@ int V4L2VideoDevice::setFormat(V4L2DeviceFormat *format)\n>>>  \t\treturn ret;\n>>>  \n>>>  \tformat_ = *format;\n>>> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n>>\n>> Looks like we were already using that pattern though ;-)\n>>\n>>>  \treturn 0;\n>>>  }\n>>>  \n>>> @@ -1325,19 +1330,17 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n>>>  \t\tplanes.push_back(std::move(plane));\n>>>  \t}\n>>>  \n>>> -\tconst auto &info = PixelFormatInfo::info(format_.fourcc);\n>>> -\tif (info.isValid() && info.numPlanes() != numPlanes) {\n>>> +\tif (formatInfo_->isValid() && formatInfo_->numPlanes() != numPlanes) {\n>>>  \t\tASSERT(numPlanes == 1u);\n>>\n>> Not for this patch, but I guess this assert needed a\n>>   \\todo Multiplanar support\n>>\n>> around it?\n> \n> No, when formatInfo_->numPlanes() != numPlanes, it means that we have a\n> multi-planar FrameBuffer with a single-planar V4L2 buffer format (e.g.\n> V4L2_PIX_FMT_NV12 as opposed to V4L2_PIX_FMT_NV12M). numPlanes must be 1\n> in that case, otherwise it's a kernel bug.\n\nAs you're updating the conditional there anyway, could you add a short\nbrief to explain that before the ASSERT please?\n\nI don't think that extra context is clear from just the if statement.\n(Perhaps there is more context outside of the hunk maybe, but it's not\nclear from just this diff).\n\n> \n>> However, this patch is just for caching the formatInfo, which seems\n>> reasonable so\n>>\n>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>>\n>>> -\t\tconst size_t numColorPlanes = info.numPlanes();\n>>> -\t\tplanes.resize(numColorPlanes);\n>>> +\t\tplanes.resize(formatInfo_->numPlanes());\n>>>  \t\tconst FileDescriptor &fd = planes[0].fd;\n>>>  \t\tsize_t offset = 0;\n>>> -\t\tfor (size_t i = 0; i < numColorPlanes; ++i) {\n>>> +\t\tfor (size_t i = 0; i < planes.size(); ++i) {\n>>>  \t\t\tplanes[i].fd = fd;\n>>>  \t\t\tplanes[i].offset = offset;\n>>>  \n>>>  \t\t\t/* \\todo Take the V4L2 stride into account */\n>>> -\t\t\tplanes[i].length = info.planeSize(format_.size, i);\n>>> +\t\t\tplanes[i].length = formatInfo_->planeSize(format_.size, i);\n>>>  \t\t\toffset += planes[i].length;\n>>>  \t\t}\n>>>  \t}\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 830C4BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 08:29:45 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D9FED6916D;\n\tFri,  3 Sep 2021 10:29:44 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7A07460251\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 10:29:43 +0200 (CEST)","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 0E3DCBBE;\n\tFri,  3 Sep 2021 10:29:43 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"mwYfK1mU\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630657783;\n\tbh=sIh6tPEqfg1BkJIbwIgIfmszLTjtguMCaO7nxhNweiE=;\n\th=From:Subject:To:Cc:References:Date:In-Reply-To:From;\n\tb=mwYfK1mUPNSJuE0CtMbydCesq7iBdJeKOGR+XPKgA/60QaD1hMs6WV98frmNxQe2z\n\tDFUS5iA0k1Vtkk5iBBiXOBTO3CpVCLH73Yi9Z/ZWxc7SbprKDEUDG/WF05UEQo2eC/\n\trYXEYD2M0MpOsrwHF/No9NtXPQkhbx1Wrg2VvqqA=","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-10-laurent.pinchart@ideasonboard.com>\n\t<ef9325cc-41f1-efa3-109d-0e5dd238317a@ideasonboard.com>\n\t<YTEXaTYw2x9VxCQQ@pendragon.ideasonboard.com>","Message-ID":"<9791e06e-b8fc-bae9-fb9c-b39f195277a4@ideasonboard.com>","Date":"Fri, 3 Sep 2021 09:29:40 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<YTEXaTYw2x9VxCQQ@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19325,"web_url":"https://patchwork.libcamera.org/comment/19325/","msgid":"<YTHfUK4YUZ9AMx2V@pendragon.ideasonboard.com>","date":"2021-09-03T08:39:44","subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Fri, Sep 03, 2021 at 09:24:02AM +0100, Kieran Bingham wrote:\n> On 02/09/2021 19:07, Laurent Pinchart wrote:\n> > On Thu, Sep 02, 2021 at 10:43:56AM +0100, Kieran Bingham wrote:\n> >> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> >>> Multi-planar frame buffers can store their planes contiguously in\n> >>> memory, or split them in discontiguous memory areas. Add a private\n> >>> function to check in which of these two categories the frame buffer\n> >>> belongs. This will be used to correctly handle the differences between\n> >>> the V4L2 single and multi planar APIs.\n> >>>\n> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> >>> ---\n> >>>  include/libcamera/internal/framebuffer.h |  2 ++\n> >>>  src/libcamera/framebuffer.cpp            | 45 +++++++++++++++++++++++-\n> >>>  2 files changed, 46 insertions(+), 1 deletion(-)\n> >>>\n> >>> diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h\n> >>> index 606aed2b4782..cd33c295466e 100644\n> >>> --- a/include/libcamera/internal/framebuffer.h\n> >>> +++ b/include/libcamera/internal/framebuffer.h\n> >>> @@ -21,9 +21,11 @@ public:\n> >>>  \tPrivate();\n> >>>  \n> >>>  \tvoid setRequest(Request *request) { request_ = request; }\n> >>> +\tbool isContiguous() const { return isContiguous_; }\n> >>>  \n> >>>  private:\n> >>>  \tRequest *request_;\n> >>> +\tbool isContiguous_;\n> >>>  };\n> >>>  \n> >>>  } /* namespace libcamera */\n> >>> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> >>> index 53ef89bf458f..99265b44da43 100644\n> >>> --- a/src/libcamera/framebuffer.cpp\n> >>> +++ b/src/libcamera/framebuffer.cpp\n> >>> @@ -106,7 +106,7 @@ LOG_DEFINE_CATEGORY(Buffer)\n> >>>   */\n> >>>  \n> >>>  FrameBuffer::Private::Private()\n> >>> -\t: request_(nullptr)\n> >>> +\t: request_(nullptr), isContiguous_(true)\n> >>>  {\n> >>>  }\n> >>>  \n> >>> @@ -120,6 +120,17 @@ FrameBuffer::Private::Private()\n> >>>   * handlers, it is called by the pipeline handlers themselves.\n> >>>   */\n> >>>  \n> >>> +/**\n> >>> + * \\fn FrameBuffer::Private::isContiguous()\n> >>> + * \\brief Check if the frame buffer stores planes contiguously in memory\n> >>> + *\n> >>> + * Multi-planar frame buffers can store their planes contiguously in memory, or\n> >>> + * split them in discontiguous memory areas. This function checks in which of\n> >>\n> >> 'split them into discontiguous'\n> >>\n> >>> + * these two categories the frame buffer belongs.\n> >>> + *\n> >>> + * \\return True if the planes are stored contiguously in memory, false otherwise\n> >>> + */\n> >>> +\n> >>>  /**\n> >>>   * \\class FrameBuffer\n> >>>   * \\brief Frame buffer data and its associated dynamic metadata\n> >>> @@ -199,9 +210,41 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> >>>  \t: Extensible(std::make_unique<Private>()), planes_(planes),\n> >>>  \t  cookie_(cookie)\n> >>>  {\n> >>> +\tunsigned int offset = 0;\n> >>> +\n> >>>  \t/* \\todo Remove the assertions after sufficient testing */\n> >>>  \tfor (const auto &plane : planes_)\n> >>>  \t\tASSERT(plane.offset != Plane::kInvalidOffset);\n> >>> +\n> >>> +\tbool isContiguous = true;\n> >>> +\tino_t inode = 0;\n> >>> +\n> >>> +\tfor (const auto &plane : planes_) {\n> >>> +\t\tif (plane.offset != offset) {\n> >>> +\t\t\tisContiguous = false;\n> >>> +\t\t\tbreak;\n> >>> +\t\t}\n> >>> +\n> >>> +\t\t/*\n> >>> +\t\t * Two different dmabuf file descriptors may still refer to the\n> >>> +\t\t * same dmabuf instance. Check this using inodes.\n> >>\n> >> Going back to the FileDescriptor::inode() extension, if that cached the\n> >> inode (it can't change) on either first call, or construction, couldn't\n> >> this whole check be simplified to:\n> >>\n> >> \t\tif (plane.fd.inode() != planes_[0].fd.inode()) {\n> >> \t\t\tisContiguous = false;\n> >> \t\t\tbreak;\n> >> \t\t}\n> > \n> > This would call fstat() every time, while the fd comparison here is\n> > meant to skip the fstat() calls in case the numerical fd match. We could\n> > do\n> \n> Did you miss the part where I said \"If we cache the inode in\n> FileDescriptor\"?\n\nNo I didn't :-)\n\n> My intention was that caching there would mean we can simplify\n> comparisons here, without incurring repeated fstat calls.\n\nIf the numerical fds are identical, there's no need to compare the\ninodes, and we can avoid calling fstat() at all.\n\n> > \t\tif (plane.fd != planes_[0].fd)\n> > \n> > if we introduced an operator==(), but as explained in a reply to the\n> > patch that adds inode(), I'd like to handle that later, to avoid\n> > blocking this series.\n> \n> Ok, I still don't see the harm in caching the inode within\n> FileDescriptor(), it can't change once read the first time.\n\nWhat we'd need to do in operator==() is to first compare the numerical\nfds, and only compare the inodes if the fds differ. Technically that's\nnot an issue, but it requires deciding what the semantics of equality is\nas explaining in a separate e-mail, and that's the part that I'd like to\ndefer to avoid delaying the offset fixes.\n\n> But, it can be deferred:\n> \n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> >>> +\t\t */\n> >>> +\t\tif (plane.fd.fd() != planes_[0].fd.fd()) {\n> >>> +\t\t\tif (!inode)\n> >>> +\t\t\t\tinode = planes_[0].fd.inode();\n> >>> +\t\t\tif (plane.fd.inode() != inode) {\n> >>> +\t\t\t\tisContiguous = false;\n> >>> +\t\t\t\tbreak;\n> >>> +\t\t\t}\n> >>> +\t\t}\n> >>> +\n> >>> +\t\toffset += plane.length;\n> >>> +\t}\n> >>> +\n> >>> +\tLOG(Buffer, Debug)\n> >>> +\t\t<< \"Buffer is \" << (isContiguous ? \"not \" : \"\") << \"contiguous\";\n> >>> +\n> >>> +\t_d()->isContiguous_ = isContiguous;\n> >>>  }\n> >>>  \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 D6ACABD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 08:40:03 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2FD1769169;\n\tFri,  3 Sep 2021 10:40:03 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 307FF69165\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 10:40:01 +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 A83D7BBE;\n\tFri,  3 Sep 2021 10:40:00 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"O7FsaA+3\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630658400;\n\tbh=kfSDNEUag026Y5HQTZ7wiBg+RmIy1brwCzr6FwsXCJI=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=O7FsaA+3CvCvewc8aXV8apb7yq+8VEwDzQaPcAfvP9SfZLG9B9qY6M/m8NCMgZLea\n\t6mNiKmK9B5tHYKL66o9CDX6BcE00CZk2jDjb9q1Dd6qB6e+VhdTjZszPjllrRxAxFS\n\tKYY76IuQ4iFWqvM47AUZTA44aVgvoBobPNyurFbc=","Date":"Fri, 3 Sep 2021 11:39:44 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTHfUK4YUZ9AMx2V@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>\n\t<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>\n\t<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>\n\t<ed14c57f-8b16-f2a0-a13f-fcfd4069897b@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<ed14c57f-8b16-f2a0-a13f-fcfd4069897b@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19329,"web_url":"https://patchwork.libcamera.org/comment/19329/","msgid":"<YTHhWnGDl1+8vBrr@pendragon.ideasonboard.com>","date":"2021-09-03T08:48:26","subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Fri, Sep 03, 2021 at 09:29:40AM +0100, Kieran Bingham wrote:\n> On 02/09/2021 19:26, Laurent Pinchart wrote:\n> > On Thu, Sep 02, 2021 at 11:21:29AM +0100, Kieran Bingham wrote:\n> >> On 02/09/2021 05:23, Laurent Pinchart wrote:\n> >>> Cache the PixelFormatInfo instead of looking it up in every call to\n> >>> createBuffer(). This prepare for usage of the info in queueBuffer(), to\n> >>\n> >> s/prepare/prepares/\n> >>\n> >>> avoid a looking every time a buffer is queued.\n> >>>\n> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> >>> ---\n> >>>  include/libcamera/internal/v4l2_videodevice.h |  1 +\n> >>>  src/libcamera/v4l2_videodevice.cpp            | 19 +++++++++++--------\n> >>>  2 files changed, 12 insertions(+), 8 deletions(-)\n> >>>\n> >>> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h\n> >>> index 7a145f608a5b..6096cd609b97 100644\n> >>> --- a/include/libcamera/internal/v4l2_videodevice.h\n> >>> +++ b/include/libcamera/internal/v4l2_videodevice.h\n> >>> @@ -243,6 +243,7 @@ private:\n> >>>  \n> >>>  \tV4L2Capability caps_;\n> >>>  \tV4L2DeviceFormat format_;\n> >>> +\tconst PixelFormatInfo *formatInfo_;\n> >>>  \n> >>>  \tenum v4l2_buf_type bufferType_;\n> >>>  \tenum v4l2_memory memoryType_;\n> >>> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> >>> index 82ddaed3656f..2d9a94c3c974 100644\n> >>> --- a/src/libcamera/v4l2_videodevice.cpp\n> >>> +++ b/src/libcamera/v4l2_videodevice.cpp\n> >>> @@ -482,8 +482,8 @@ const std::string V4L2DeviceFormat::toString() const\n> >>>   * \\param[in] deviceNode The file-system path to the video device node\n> >>>   */\n> >>>  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> >>> -\t: V4L2Device(deviceNode), cache_(nullptr), fdBufferNotifier_(nullptr),\n> >>> -\t  streaming_(false)\n> >>> +\t: V4L2Device(deviceNode), formatInfo_(nullptr), cache_(nullptr),\n> >>> +\t  fdBufferNotifier_(nullptr), streaming_(false)\n> >>>  {\n> >>>  \t/*\n> >>>  \t * We default to an MMAP based CAPTURE video device, however this will\n> >>> @@ -586,6 +586,7 @@ int V4L2VideoDevice::open()\n> >>>  \t\treturn ret;\n> >>>  \t}\n> >>>  \n> >>> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> >>\n> >> I shudder a little seeing code hugging the return like that ;-)\n> >>\n> >> But it's purely subjective, same below of course.\n> > \n> > We have various patterns indeed :-) I haven't been able to establish yet\n> > what my mental process to deal with these are, sometimes it bothers me\n> > too, but not always.\n> > \n> >>>  \treturn 0;\n> >>>  }\n> >>>  \n> >>> @@ -681,6 +682,7 @@ int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)\n> >>>  \t\treturn ret;\n> >>>  \t}\n> >>>  \n> >>> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> >>>  \treturn 0;\n> >>>  }\n> >>>  \n> >>> @@ -695,6 +697,8 @@ void V4L2VideoDevice::close()\n> >>>  \treleaseBuffers();\n> >>>  \tdelete fdBufferNotifier_;\n> >>>  \n> >>> +\tformatInfo_ = nullptr;\n> >>> +\n> >>>  \tV4L2Device::close();\n> >>>  }\n> >>>  \n> >>> @@ -787,6 +791,7 @@ int V4L2VideoDevice::setFormat(V4L2DeviceFormat *format)\n> >>>  \t\treturn ret;\n> >>>  \n> >>>  \tformat_ = *format;\n> >>> +\tformatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> >>\n> >> Looks like we were already using that pattern though ;-)\n> >>\n> >>>  \treturn 0;\n> >>>  }\n> >>>  \n> >>> @@ -1325,19 +1330,17 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> >>>  \t\tplanes.push_back(std::move(plane));\n> >>>  \t}\n> >>>  \n> >>> -\tconst auto &info = PixelFormatInfo::info(format_.fourcc);\n> >>> -\tif (info.isValid() && info.numPlanes() != numPlanes) {\n> >>> +\tif (formatInfo_->isValid() && formatInfo_->numPlanes() != numPlanes) {\n> >>>  \t\tASSERT(numPlanes == 1u);\n> >>\n> >> Not for this patch, but I guess this assert needed a\n> >>   \\todo Multiplanar support\n> >>\n> >> around it?\n> > \n> > No, when formatInfo_->numPlanes() != numPlanes, it means that we have a\n> > multi-planar FrameBuffer with a single-planar V4L2 buffer format (e.g.\n> > V4L2_PIX_FMT_NV12 as opposed to V4L2_PIX_FMT_NV12M). numPlanes must be 1\n> > in that case, otherwise it's a kernel bug.\n> \n> As you're updating the conditional there anyway, could you add a short\n> brief to explain that before the ASSERT please?\n> \n> I don't think that extra context is clear from just the if statement.\n> (Perhaps there is more context outside of the hunk maybe, but it's not\n> clear from just this diff).\n\nThat's because this diff only deals with format caching :-) It's a good\npoint though, it's not trivial so I'll add a separate patch to document\nthis properly.\n\n> >> However, this patch is just for caching the formatInfo, which seems\n> >> reasonable so\n> >>\n> >> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >>\n> >>> -\t\tconst size_t numColorPlanes = info.numPlanes();\n> >>> -\t\tplanes.resize(numColorPlanes);\n> >>> +\t\tplanes.resize(formatInfo_->numPlanes());\n> >>>  \t\tconst FileDescriptor &fd = planes[0].fd;\n> >>>  \t\tsize_t offset = 0;\n> >>> -\t\tfor (size_t i = 0; i < numColorPlanes; ++i) {\n> >>> +\t\tfor (size_t i = 0; i < planes.size(); ++i) {\n> >>>  \t\t\tplanes[i].fd = fd;\n> >>>  \t\t\tplanes[i].offset = offset;\n> >>>  \n> >>>  \t\t\t/* \\todo Take the V4L2 stride into account */\n> >>> -\t\t\tplanes[i].length = info.planeSize(format_.size, i);\n> >>> +\t\t\tplanes[i].length = formatInfo_->planeSize(format_.size, i);\n> >>>  \t\t\toffset += planes[i].length;\n> >>>  \t\t}\n> >>>  \t}","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 E72FCBD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 08:48:45 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 983246916B;\n\tFri,  3 Sep 2021 10:48:45 +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 B1F2D69165\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 10:48:43 +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 4ABE0BBE;\n\tFri,  3 Sep 2021 10:48:43 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"N8Bz8UWG\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630658923;\n\tbh=NVmHskBjDTeuGvv73Me0liuS0Cn4usIjG3AhJiGiImw=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=N8Bz8UWG9G4tGxdL8Wo+z0G7mmVbhbDNCv0Zg8dVKKM2p7CF1dbsoltPUPxHnTmDG\n\trDmNQwzdO0uIspN3A7jZ0+ZWtANhh7OhF/FoMKz9DlJ+XaB1WfLAS8wdHPsmymYcmM\n\tne03QpHQOwq9rZx+cKGu2IojjIW7A07VtiAq9G5I=","Date":"Fri, 3 Sep 2021 11:48:26 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YTHhWnGDl1+8vBrr@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-10-laurent.pinchart@ideasonboard.com>\n\t<ef9325cc-41f1-efa3-109d-0e5dd238317a@ideasonboard.com>\n\t<YTEXaTYw2x9VxCQQ@pendragon.ideasonboard.com>\n\t<9791e06e-b8fc-bae9-fb9c-b39f195277a4@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<9791e06e-b8fc-bae9-fb9c-b39f195277a4@ideasonboard.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","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":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19345,"web_url":"https://patchwork.libcamera.org/comment/19345/","msgid":"<CAO5uPHO0ZnWfZ8Pr=QNZ=Wgvru7fWsoSmNFPzXuQEDbg+yRQXw@mail.gmail.com>","date":"2021-09-03T10:35:12","subject":"Re: [libcamera-devel] [RFC PATCH v1 03/12] libcamera: formats: Add\n\tplaneSize() helpers to PixelFormatInfo","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent, thank you for the patch.\n\nOn Thu, Sep 2, 2021 at 5:49 PM Kieran Bingham\n<kieran.bingham@ideasonboard.com> wrote:\n>\n> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > Add two helpers functions to the PixelFormatInfo class to compute the\n> > byte size of a given plane, taking the frame size, the stride, the\n> > alignement constraints and the vertical subsampling into account.\n>\n> s/alignement/alignment/\n>\n>\n> > Use the new functions through the code base to replace manual\n> > implementations.\n>\n>\n> \\o/ that makes me happy.\n>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/internal/formats.h     |  4 ++\n> >  src/android/mm/generic_camera_buffer.cpp | 11 +---\n> >  src/android/yuv/post_processor_yuv.cpp   | 10 ++-\n> >  src/libcamera/formats.cpp                | 82 ++++++++++++++++++++----\n> >  src/libcamera/v4l2_videodevice.cpp       |  6 +-\n> >  5 files changed, 79 insertions(+), 34 deletions(-)\n> >\n> > diff --git a/include/libcamera/internal/formats.h b/include/libcamera/internal/formats.h\n> > index 51a8a6b8b0ae..8f314e99889b 100644\n> > --- a/include/libcamera/internal/formats.h\n> > +++ b/include/libcamera/internal/formats.h\n> > @@ -42,6 +42,10 @@ public:\n> >\n> >       unsigned int stride(unsigned int width, unsigned int plane,\n> >                           unsigned int align = 1) const;\n> > +     unsigned int planeSize(const Size &size, unsigned int plane,\n> > +                            unsigned int align = 1) const;\n> > +     unsigned int planeSize(unsigned int height, unsigned int plane,\n> > +                            unsigned int stride) const;\n> >       unsigned int frameSize(const Size &size, unsigned int align = 1) const;\n> >       unsigned int frameSize(const Size &size,\n> >                              const std::array<unsigned int, 3> &strides) const;\n> > diff --git a/src/android/mm/generic_camera_buffer.cpp b/src/android/mm/generic_camera_buffer.cpp\n> > index 22efc4d4b13a..93aa5821e470 100644\n> > --- a/src/android/mm/generic_camera_buffer.cpp\n> > +++ b/src/android/mm/generic_camera_buffer.cpp\n> > @@ -108,16 +108,9 @@ CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer,\n> >\n> >       unsigned int offset = 0;\n> >       for (unsigned int i = 0; i < numPlanes; ++i) {\n> > -             /*\n> > -              * \\todo Remove if this plane size computation function is\n> > -              * added to PixelFormatInfo.\n> > -              */\n> > -             const unsigned int vertSubSample = info.planes[i].verticalSubSampling;\n> > -             const unsigned int stride = info.stride(size.width, i, 1u);\n> > -             const unsigned int planeSize =\n> > -                     stride * ((size.height + vertSubSample - 1) / vertSubSample);\n> > +             const unsigned int planeSize = info.planeSize(size, i);\n> >\n> > -             planeInfo_[i].stride = stride;\n> > +             planeInfo_[i].stride = info.stride(size.width, i, 1u);\n> >               planeInfo_[i].offset = offset;\n> >               planeInfo_[i].size = planeSize;\n> >\n> > diff --git a/src/android/yuv/post_processor_yuv.cpp b/src/android/yuv/post_processor_yuv.cpp\n> > index 6952fc38b0ef..7b3b49609cb1 100644\n> > --- a/src/android/yuv/post_processor_yuv.cpp\n> > +++ b/src/android/yuv/post_processor_yuv.cpp\n> > @@ -134,11 +134,9 @@ void PostProcessorYuv::calculateLengths(const StreamConfiguration &inCfg,\n> >               sourceStride_[i] = inCfg.stride;\n> >               destinationStride_[i] = nv12Info.stride(destinationSize_.width, i, 1);\n> >\n> > -             const unsigned int vertSubSample =\n> > -                     nv12Info.planes[i].verticalSubSampling;\n> > -             sourceLength_[i] = sourceStride_[i] *\n> > -                     ((sourceSize_.height + vertSubSample - 1) / vertSubSample);\n> > -             destinationLength_[i] = destinationStride_[i] *\n> > -                     ((destinationSize_.height + vertSubSample - 1) / vertSubSample);\n> > +             sourceLength_[i] = nv12Info.planeSize(sourceSize_.height, i,\n> > +                                                   sourceStride_[i]);\n> > +             destinationLength_[i] = nv12Info.planeSize(destinationSize_.height, i,\n> > +                                                        destinationStride_[i]);\n> >       }\n> >  }\n> > diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp\n> > index 603d88619fe0..76be93bc1c5c 100644\n> > --- a/src/libcamera/formats.cpp\n> > +++ b/src/libcamera/formats.cpp\n> > @@ -11,6 +11,7 @@\n> >  #include <errno.h>\n> >\n> >  #include <libcamera/base/log.h>\n> > +#include <libcamera/base/utils.h>\n> >\n> >  #include <libcamera/formats.h>\n> >\n> > @@ -801,32 +802,85 @@ unsigned int PixelFormatInfo::stride(unsigned int width, unsigned int plane,\n> >  }\n> >\n> >  /**\n> > - * \\brief Compute the number of bytes necessary to store a frame\n> > + * \\brief Compute the number of bytes necessary to store a plane of a frame\n> >   * \\param[in] size The size of the frame, in pixels\n> > + * \\param[in] plane The plane index\n> >   * \\param[in] align The stride alignment, in bytes (1 for default alignment)\n> >   *\n> > - * The frame is computed by adding the product of the line stride and the frame\n> > - * height for all planes, taking subsampling and other format characteristics\n> > - * into account. Additional stride alignment constraints may be specified\n> > - * through the \\a align parameter, and will apply to all planes. For more\n> > - * complex stride constraints, use the frameSize() overloaded version that takes\n> > - * an array of stride values.\n> > + * The plane size is computed by multiplying the line stride and the frame\n> > + * height, taking subsampling and other format characteristics into account.\n> > + * Stride alignment constraints may be specified through the \\a align parameter.\n> >   *\n> >   * \\sa stride()\n> >   *\n> > + * \\return The number of bytes necessary to store the plane, or 0 if the\n> > + * PixelFormatInfo instance is not valid or the plane number isn't valid for the\n> > + * format\n> > + */\n> > +unsigned int PixelFormatInfo::planeSize(const Size &size, unsigned int plane,\n> > +                                     unsigned int align) const\n> > +{\n> > +     unsigned int stride = PixelFormatInfo::stride(size.width, plane, align);\n> > +     if (!stride)\n> > +             return 0;\n> > +\n\nThe below can be replaced with\nreturn planeSize(size.height, plane, stride);\n\nNice clean up!\n\nReviewed-by: Hirokazu Honda <hiroh@chromium.org>\n\n-Hiro\n> > +     unsigned int vertSubSample = planes[plane].verticalSubSampling;\n> > +     if (!vertSubSample)\n> > +             return 0;\n> > +\n> > +     /* stride * ceil(height / verticalSubSampling) */\n> > +     return stride * ((size.height + vertSubSample - 1) / vertSubSample);\n> > +}\n> > +\n> > +/**\n> > + * \\brief Compute the number of bytes necessary to store a plane of a frame\n> > + * \\param[in] height The height of the frame, in pixels\n> > + * \\param[in] plane The plane index\n> > + * \\param[in] stride The plane stride, in bytes\n> > + *\n> > + * The plane size is computed by multiplying the line stride and the frame\n> > + * height, taking subsampling and other format characteristics into account.\n> > + * Stride alignment constraints may be specified through the \\a align parameter.\n> > + *\n> > + * \\return The number of bytes necessary to store the plane, or 0 if the\n> > + * PixelFormatInfo instance is not valid or the plane number isn't valid for the\n> > + * format\n> > + */\n> > +unsigned int PixelFormatInfo::planeSize(unsigned int height, unsigned int plane,\n> > +                                     unsigned int stride) const\n> > +{\n> > +     unsigned int vertSubSample = planes[plane].verticalSubSampling;\n> > +     if (!vertSubSample)\n> > +             return 0;\n> > +\n> > +     /* stride * ceil(height / verticalSubSampling) */\n> > +     return stride * ((height + vertSubSample - 1) / vertSubSample);\n> > +}\n> > +\n> > +/**\n> > + * \\brief Compute the number of bytes necessary to store a frame\n> > + * \\param[in] size The size of the frame, in pixels\n> > + * \\param[in] align The stride alignment, in bytes (1 for default alignment)\n> > + *\n> > + * The frame size is computed by adding the size of all planes, as computed by\n> > + * planeSize(), using the specified alignment constraints for all planes. For\n> > + * more complex stride constraints, use the frameSize() overloaded version that\n> > + * takes an array of stride values.\n> > + *\n> > + * \\sa planeSize()\n> > + *\n> >   * \\return The number of bytes necessary to store the frame, or 0 if the\n> >   * PixelFormatInfo instance is not valid\n> >   */\n> >  unsigned int PixelFormatInfo::frameSize(const Size &size, unsigned int align) const\n> >  {\n> > -     /* stride * ceil(height / verticalSubSampling) */\n> >       unsigned int sum = 0;\n> > -     for (unsigned int i = 0; i < 3; i++) {\n> > -             unsigned int vertSubSample = planes[i].verticalSubSampling;\n> > -             if (!vertSubSample)\n> > -                     continue;\n> > -             sum += stride(size.width, i, align)\n> > -                  * ((size.height + vertSubSample - 1) / vertSubSample);\n> > +\n> > +     for (const auto &[i, plane] : utils::enumerate(planes)) {\n> > +             if (plane.bytesPerGroup == 0)\n> > +                     break;\n> > +\n> > +             sum += planeSize(size, i, align);\n> >       }\n> >\n> >       return sum;\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 4e1c2b7cef5e..adabd4720668 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -1337,11 +1337,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> >                       planes[i].offset = offset;\n> >\n> >                       /* \\todo Take the V4L2 stride into account */\n> > -                     const unsigned int vertSubSample =\n> > -                             info.planes[i].verticalSubSampling;\n> > -                     planes[i].length =\n> > -                             info.stride(format_.size.width, i, 1u) *\n> > -                             ((format_.size.height + vertSubSample - 1) / vertSubSample);\n> > +                     planes[i].length = info.planeSize(format_.size, i);\n> >                       offset += planes[i].length;\n> >               }\n> >       }\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 58233BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 10:35:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 227F66916A;\n\tFri,  3 Sep 2021 12:35:25 +0200 (CEST)","from mail-ej1-x634.google.com (mail-ej1-x634.google.com\n\t[IPv6:2a00:1450:4864:20::634])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A116769167\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 12:35:23 +0200 (CEST)","by mail-ej1-x634.google.com with SMTP id lc21so11152141ejc.7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 03:35:23 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"e3WOCihA\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=6Xnfl2x3XKo8AO1nN4efjM3hjOfOu92eIQolCq3dCvc=;\n\tb=e3WOCihAJkFl+1CzIfZG6VXOJNVIzuIXrcf2iBY9+DMLWaMq5ddzo8mjl3eXrx9TS7\n\tiLqSi0Vni05n6B1Vbt0LEPvP1pKsII4i2P8JCP+V1wKHZUpt5XZgNRpLhZ49kMrQoro7\n\tYyDb/ijvsOmFq2+UXucwME/s2wKlO5WD9RNqY=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=6Xnfl2x3XKo8AO1nN4efjM3hjOfOu92eIQolCq3dCvc=;\n\tb=RU21Hx2kGUnuqFGxXHWZmXDmBX0P/alkaPW/HYX5IJVGiNizClAnxDS5G2z/Q3HgKe\n\tdGTqoVW9JEj5FWfKpQVrL983lCqtWNFf6zqVNsMV7Adt1/dB+8en59RmgxBFNd0qHUrx\n\thePUzelxuFlqAAfQaS121PNeUqHhbeC9hxfXxbT4e+hZibSaA63LPvkonCmSTfOXar0X\n\tSaCb23sLpj2LWcIwutbkzl37rlhefR7En1bNzkMyaKMg7qZUl7LS5LtP1q6a6mwuGwLk\n\tbeE2zus38R75Tch1XrbHO6Iflfoem1WvObnIuzKoUHamwntQ+T2lLw9bDyHIF8ds/seB\n\tCM3w==","X-Gm-Message-State":"AOAM53027ooW1Qcr/J8WB/au+tq2Zdni4QpThsK4F9HJ52xtR1rCFuyv\n\tHaMdO3i/bborjYEvhFveEKrwNX0aTlIfAmvwXHl6AMIu+IT9og==","X-Google-Smtp-Source":"ABdhPJzUrNx6x/+HUjt3xi3YfNScdEmxvpsh+HVvZ9RbCiqhZq/lX2bf+AVZW0yhRSQgXJ1oMU7BiGgwIbsO12CN1vs=","X-Received":"by 2002:a17:906:3adb:: with SMTP id\n\tz27mr3391718ejd.291.1630665323184; \n\tFri, 03 Sep 2021 03:35:23 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-4-laurent.pinchart@ideasonboard.com>\n\t<65e06923-3532-432d-3f13-21044379d83a@ideasonboard.com>","In-Reply-To":"<65e06923-3532-432d-3f13-21044379d83a@ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 19:35:12 +0900","Message-ID":"<CAO5uPHO0ZnWfZ8Pr=QNZ=Wgvru7fWsoSmNFPzXuQEDbg+yRQXw@mail.gmail.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 03/12] libcamera: formats: Add\n\tplaneSize() helpers to PixelFormatInfo","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19347,"web_url":"https://patchwork.libcamera.org/comment/19347/","msgid":"<CAO5uPHPBeoK7HDoL-uDWA51Afakebw17rX3zrarpywZ9MEqqew@mail.gmail.com>","date":"2021-09-03T10:53:48","subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"On Fri, Sep 3, 2021 at 3:36 AM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Kieran,\n>\n> On Thu, Sep 02, 2021 at 10:23:48AM +0100, Kieran Bingham wrote:\n> > On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > > V4L2 describes multi-planar formats with different 4CCs depending on\n> > > whether or not the planes are stored contiguously in memory. Support\n> > > this when translating between PixelFormat and V4L2PixelFormat.\n> > >\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > >  include/libcamera/internal/formats.h |   2 +-\n> > >  src/libcamera/formats.cpp            | 141 ++++++++++++++++-----------\n> > >  src/libcamera/pipeline/ipu3/cio2.cpp |   2 +-\n> > >  src/libcamera/v4l2_pixelformat.cpp   |  11 ++-\n> > >  src/v4l2/v4l2_camera_proxy.cpp       |   6 +-\n> > >  5 files changed, 99 insertions(+), 63 deletions(-)\n> > >\n> > > diff --git a/include/libcamera/internal/formats.h b/include/libcamera/internal/formats.h\n> > > index 8f314e99889b..f9ce5f0834fa 100644\n> > > --- a/include/libcamera/internal/formats.h\n> > > +++ b/include/libcamera/internal/formats.h\n> > > @@ -55,7 +55,7 @@ public:\n> > >     /* \\todo Add support for non-contiguous memory planes */\n> > >     const char *name;\n> > >     PixelFormat format;\n> > > -   V4L2PixelFormat v4l2Format;\n> > > +   std::array<V4L2PixelFormat, 2> v4l2Format;\n> >\n> > As discussed below, I'm curious if a struct would be clearer here and\n> > help prevent bugs later...\n> >\n> >       struct {\n> >               V4L2PixelFormat planar;\n> >               V4L2PixelFormat multiplanar;\n> >       } v4l2Format;\n> >\n> > >     unsigned int bitsPerPixel;\n> > >     enum ColourEncoding colourEncoding;\n> > >     bool packed;\n> > > diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp\n> > > index 76be93bc1c5c..ce7c5a2d267e 100644\n> > > --- a/src/libcamera/formats.cpp\n> > > +++ b/src/libcamera/formats.cpp\n> > > @@ -56,7 +56,14 @@ LOG_DEFINE_CATEGORY(Formats)\n> > >   * \\brief The PixelFormat described by this instance\n> > >   *\n> > >   * \\var PixelFormatInfo::v4l2Format\n> > > - * \\brief The V4L2 pixel format corresponding to the PixelFormat\n> > > + * \\brief The V4L2 pixel formats corresponding to the PixelFormat\n> > > + *\n> > > + * Multiple V4L2 formats may exist for one PixelFormat when the format uses\n> > > + * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate\n> > > + * planes formats. The two entries in the array store the contiguous and\n> > > + * non-contiguous V4L2 formats respectively. If the PixelFormat isn't a\n> > > + * multiplanar format, or if no corresponding non-contiguous V4L2 format\n> > > + * exists, the second entry is invalid.\n> > >   *\n> > >   * \\var PixelFormatInfo::bitsPerPixel\n> > >   * \\brief The average number of bits per pixel\n> > > @@ -149,7 +156,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::RGB565, {\n> > >             .name = \"RGB565\",\n> > >             .format = formats::RGB565,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -159,7 +166,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::RGB565_BE, {\n> > >             .name = \"RGB565_BE\",\n> > >             .format = formats::RGB565_BE,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565X),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565X), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -169,7 +176,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::BGR888, {\n> > >             .name = \"BGR888\",\n> > >             .format = formats::BGR888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB24), },\n> > >             .bitsPerPixel = 24,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -179,7 +186,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::RGB888, {\n> > >             .name = \"RGB888\",\n> > >             .format = formats::RGB888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGR24), },\n> > >             .bitsPerPixel = 24,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -189,7 +196,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::XRGB8888, {\n> > >             .name = \"XRGB8888\",\n> > >             .format = formats::XRGB8888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XBGR32),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XBGR32), },\n> > >             .bitsPerPixel = 32,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -199,7 +206,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::XBGR8888, {\n> > >             .name = \"XBGR8888\",\n> > >             .format = formats::XBGR8888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBX32),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBX32), },\n> > >             .bitsPerPixel = 32,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -209,7 +216,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::BGRX8888, {\n> > >             .name = \"BGRX8888\",\n> > >             .format = formats::BGRX8888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XRGB32),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XRGB32), },\n> > >             .bitsPerPixel = 32,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -219,7 +226,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::ABGR8888, {\n> > >             .name = \"ABGR8888\",\n> > >             .format = formats::ABGR8888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBA32), },\n> > >             .bitsPerPixel = 32,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -229,7 +236,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::ARGB8888, {\n> > >             .name = \"ARGB8888\",\n> > >             .format = formats::ARGB8888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ABGR32), },\n> > >             .bitsPerPixel = 32,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -239,7 +246,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::BGRA8888, {\n> > >             .name = \"BGRA8888\",\n> > >             .format = formats::BGRA8888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ARGB32), },\n> > >             .bitsPerPixel = 32,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -249,7 +256,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::RGBA8888, {\n> > >             .name = \"RGBA8888\",\n> > >             .format = formats::RGBA8888,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGRA32), },\n> > >             .bitsPerPixel = 32,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > >             .packed = false,\n> > > @@ -261,7 +268,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::YUYV, {\n> > >             .name = \"YUYV\",\n> > >             .format = formats::YUYV,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YUYV), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -271,7 +278,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::YVYU, {\n> > >             .name = \"YVYU\",\n> > >             .format = formats::YVYU,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YVYU), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -281,7 +288,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::UYVY, {\n> > >             .name = \"UYVY\",\n> > >             .format = formats::UYVY,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_UYVY), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -291,7 +298,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::VYUY, {\n> > >             .name = \"VYUY\",\n> > >             .format = formats::VYUY,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_VYUY), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -303,7 +310,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::NV12, {\n> > >             .name = \"NV12\",\n> > >             .format = formats::NV12,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > > +           .v4l2Format = {\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV12M),\n> > > +           },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -313,7 +323,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::NV21, {\n> > >             .name = \"NV21\",\n> > >             .format = formats::NV21,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > > +           .v4l2Format = {\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV21M),\n> > > +           },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -323,7 +336,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::NV16, {\n> > >             .name = \"NV16\",\n> > >             .format = formats::NV16,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > > +           .v4l2Format = {\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV16M),\n> > > +           },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -333,7 +349,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::NV61, {\n> > >             .name = \"NV61\",\n> > >             .format = formats::NV61,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > > +           .v4l2Format = {\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV61M),\n> > > +           },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -343,7 +362,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::NV24, {\n> > >             .name = \"NV24\",\n> > >             .format = formats::NV24,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV24),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV24), },\n> > >             .bitsPerPixel = 24,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -353,7 +372,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::NV42, {\n> > >             .name = \"NV42\",\n> > >             .format = formats::NV42,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV42),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV42), },\n> > >             .bitsPerPixel = 24,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -363,7 +382,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::YUV420, {\n> > >             .name = \"YUV420\",\n> > >             .format = formats::YUV420,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > > +           .v4l2Format = {\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV420M),\n> > > +           },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -373,7 +395,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::YVU420, {\n> > >             .name = \"YVU420\",\n> > >             .format = formats::YVU420,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > > +           .v4l2Format = {\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YVU420M),\n> > > +           },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -383,7 +408,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::YUV422, {\n> > >             .name = \"YUV422\",\n> > >             .format = formats::YUV422,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > > +           .v4l2Format = {\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV422M),\n> > > +           },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -395,7 +423,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::R8, {\n> > >             .name = \"R8\",\n> > >             .format = formats::R8,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_GREY),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_GREY), },\n> > >             .bitsPerPixel = 8,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -407,7 +435,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SBGGR8, {\n> > >             .name = \"SBGGR8\",\n> > >             .format = formats::SBGGR8,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), },\n> > >             .bitsPerPixel = 8,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -417,7 +445,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGBRG8, {\n> > >             .name = \"SGBRG8\",\n> > >             .format = formats::SGBRG8,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), },\n> > >             .bitsPerPixel = 8,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -427,7 +455,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGRBG8, {\n> > >             .name = \"SGRBG8\",\n> > >             .format = formats::SGRBG8,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), },\n> > >             .bitsPerPixel = 8,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -437,7 +465,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SRGGB8, {\n> > >             .name = \"SRGGB8\",\n> > >             .format = formats::SRGGB8,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), },\n> > >             .bitsPerPixel = 8,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -447,7 +475,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SBGGR10, {\n> > >             .name = \"SBGGR10\",\n> > >             .format = formats::SBGGR10,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -457,7 +485,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGBRG10, {\n> > >             .name = \"SGBRG10\",\n> > >             .format = formats::SGBRG10,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -467,7 +495,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGRBG10, {\n> > >             .name = \"SGRBG10\",\n> > >             .format = formats::SGRBG10,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -477,7 +505,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SRGGB10, {\n> > >             .name = \"SRGGB10\",\n> > >             .format = formats::SRGGB10,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -487,7 +515,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SBGGR10_CSI2P, {\n> > >             .name = \"SBGGR10_CSI2P\",\n> > >             .format = formats::SBGGR10_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -497,7 +525,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGBRG10_CSI2P, {\n> > >             .name = \"SGBRG10_CSI2P\",\n> > >             .format = formats::SGBRG10_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -507,7 +535,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGRBG10_CSI2P, {\n> > >             .name = \"SGRBG10_CSI2P\",\n> > >             .format = formats::SGRBG10_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -517,7 +545,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SRGGB10_CSI2P, {\n> > >             .name = \"SRGGB10_CSI2P\",\n> > >             .format = formats::SRGGB10_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -527,7 +555,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SBGGR12, {\n> > >             .name = \"SBGGR12\",\n> > >             .format = formats::SBGGR12,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -537,7 +565,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGBRG12, {\n> > >             .name = \"SGBRG12\",\n> > >             .format = formats::SGBRG12,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -547,7 +575,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGRBG12, {\n> > >             .name = \"SGRBG12\",\n> > >             .format = formats::SGRBG12,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -557,7 +585,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SRGGB12, {\n> > >             .name = \"SRGGB12\",\n> > >             .format = formats::SRGGB12,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -567,7 +595,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SBGGR12_CSI2P, {\n> > >             .name = \"SBGGR12_CSI2P\",\n> > >             .format = formats::SBGGR12_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -577,7 +605,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGBRG12_CSI2P, {\n> > >             .name = \"SGBRG12_CSI2P\",\n> > >             .format = formats::SGBRG12_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -587,7 +615,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGRBG12_CSI2P, {\n> > >             .name = \"SGRBG12_CSI2P\",\n> > >             .format = formats::SGRBG12_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -597,7 +625,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SRGGB12_CSI2P, {\n> > >             .name = \"SRGGB12_CSI2P\",\n> > >             .format = formats::SRGGB12_CSI2P,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), },\n> > >             .bitsPerPixel = 12,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -607,7 +635,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SBGGR16, {\n> > >             .name = \"SBGGR16\",\n> > >             .format = formats::SBGGR16,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -617,7 +645,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGBRG16, {\n> > >             .name = \"SGBRG16\",\n> > >             .format = formats::SGBRG16,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -627,7 +655,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGRBG16, {\n> > >             .name = \"SGRBG16\",\n> > >             .format = formats::SGRBG16,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -637,7 +665,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SRGGB16, {\n> > >             .name = \"SRGGB16\",\n> > >             .format = formats::SRGGB16,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), },\n> > >             .bitsPerPixel = 16,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = false,\n> > > @@ -647,7 +675,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SBGGR10_IPU3, {\n> > >             .name = \"SBGGR10_IPU3\",\n> > >             .format = formats::SBGGR10_IPU3,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -658,7 +686,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGBRG10_IPU3, {\n> > >             .name = \"SGBRG10_IPU3\",\n> > >             .format = formats::SGBRG10_IPU3,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -668,7 +696,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SGRBG10_IPU3, {\n> > >             .name = \"SGRBG10_IPU3\",\n> > >             .format = formats::SGRBG10_IPU3,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -678,7 +706,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::SRGGB10_IPU3, {\n> > >             .name = \"SRGGB10_IPU3\",\n> > >             .format = formats::SRGGB10_IPU3,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10), },\n> > >             .bitsPerPixel = 10,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > >             .packed = true,\n> > > @@ -690,7 +718,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > >     { formats::MJPEG, {\n> > >             .name = \"MJPEG\",\n> > >             .format = formats::MJPEG,\n> > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),\n> > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_MJPEG), },\n> > >             .bitsPerPixel = 0,\n> > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > >             .packed = false,\n> > > @@ -736,7 +764,8 @@ const PixelFormatInfo &PixelFormatInfo::info(const V4L2PixelFormat &format)\n> > >  {\n> > >     const auto &info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),\n> > >                                     [format](auto pair) {\n> > > -                                           return pair.second.v4l2Format == format;\n> > > +                                           return pair.second.v4l2Format[0] == format ||\n> > > +                                                  pair.second.v4l2Format[1] == format;\n> > >                                     });\n> > >     if (info == pixelFormatInfo.end())\n> > >             return pixelFormatInfoInvalid;\n> > > diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > index 9cedcb5b2879..5a9cffc80c8d 100644\n> > > --- a/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > +++ b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > @@ -205,7 +205,7 @@ int CIO2Device::configure(const Size &size, V4L2DeviceFormat *outputFormat)\n> > >\n> > >     const PixelFormatInfo &info = PixelFormatInfo::info(itInfo->second);\n> > >\n> > > -   outputFormat->fourcc = info.v4l2Format;\n> > > +   outputFormat->fourcc = info.v4l2Format[0];\n\nI probably miss something here. Why does selecting single planar\nformat always work here?\nDitto for v4l2_camera_proxy.cpp\n\nBest Regards,\n-Hiro\n> > >     outputFormat->size = sensorFormat.size;\n> > >     outputFormat->planesCount = 1;\n> > >\n> > > diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp\n> > > index 93fc4446cc64..93ead8928ed7 100644\n> > > --- a/src/libcamera/v4l2_pixelformat.cpp\n> > > +++ b/src/libcamera/v4l2_pixelformat.cpp\n> > > @@ -67,12 +67,19 @@ const std::map<V4L2PixelFormat, PixelFormat> vpf2pf{\n> > >\n> > >     /* YUV planar formats. */\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV16), formats::NV16 },\n> > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV16M), formats::NV16 },\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV61), formats::NV61 },\n> > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV61M), formats::NV61 },\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV12), formats::NV12 },\n> > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV12M), formats::NV12 },\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV21), formats::NV21 },\n> > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV21M), formats::NV21 },\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_YUV420), formats::YUV420 },\n> > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YUV420M), formats::YUV420 },\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_YVU420), formats::YVU420 },\n> > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YVU420M), formats::YVU420 },\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_YUV422P), formats::YUV422 },\n> > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YUV422M), formats::YUV422 },\n> >\n> > I'm worried that we're losing information here.\n> > But presumably it's handled by whether we use mplane or not on the\n> > device...?\n>\n> We don't expose to applications whether the camera requires contiguous\n> buffers or can support non-contiguous buffers. We don't otherwise lose\n> information, as we pick a specific V4L2 format from the PixelFormat,\n> based on an explicit selection of multiplanar or singleplanar V4L2\n> format by the pipeline handler.\n>\n> > >     /* Greyscale formats. */\n> > >     { V4L2PixelFormat(V4L2_PIX_FMT_GREY), formats::R8 },\n> > > @@ -202,13 +209,13 @@ PixelFormat V4L2PixelFormat::toPixelFormat() const\n> > >   * \\return The V4L2PixelFormat corresponding to \\a pixelFormat\n> > >   */\n> > >  V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat,\n> > > -                                            [[maybe_unused]] bool multiplanar)\n> > > +                                            bool multiplanar)\n> > >  {\n> > >     const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);\n> > >     if (!info.isValid())\n> > >             return V4L2PixelFormat();\n> > >\n> > > -   return info.v4l2Format;\n> > > +   return info.v4l2Format[multiplanar ? 1 : 0];\n> >\n> > I was going to say, a struct with named fields of 'planar', and\n> > 'multiplanar' might be a better, though I like the simplicity of this bit.\n>\n> Would be easy to rewrite though.\n>\n> > >  }\n> > >\n> > >  } /* namespace libcamera */\n> > > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> > > index 7682c4bddf90..8d8ee395954f 100644\n> > > --- a/src/v4l2/v4l2_camera_proxy.cpp\n> > > +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> > > @@ -169,7 +169,7 @@ void V4L2CameraProxy::setFmtFromConfig(const StreamConfiguration &streamConfig)\n> > >\n> > >     v4l2PixFormat_.width        = size.width;\n> > >     v4l2PixFormat_.height       = size.height;\n> > > -   v4l2PixFormat_.pixelformat  = info.v4l2Format;\n> > > +   v4l2PixFormat_.pixelformat  = info.v4l2Format[0];\n> > >     v4l2PixFormat_.field        = V4L2_FIELD_NONE;\n> > >     v4l2PixFormat_.bytesperline = streamConfig.stride;\n> > >     v4l2PixFormat_.sizeimage    = streamConfig.frameSize;\n> > > @@ -276,7 +276,7 @@ int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *file, struct v4l2_fmtdesc *\n> > >     /* \\todo Add map from format to description. */\n> > >     utils::strlcpy(reinterpret_cast<char *>(arg->description),\n> > >                    \"Video Format Description\", sizeof(arg->description));\n> > > -   arg->pixelformat = PixelFormatInfo::info(format).v4l2Format;\n> > > +   arg->pixelformat = PixelFormatInfo::info(format).v4l2Format[0];\n> > >\n> > >     memset(arg->reserved, 0, sizeof(arg->reserved));\n> > >\n> > > @@ -315,7 +315,7 @@ int V4L2CameraProxy::tryFormat(struct v4l2_format *arg)\n> > >\n> > >     arg->fmt.pix.width        = config.size.width;\n> > >     arg->fmt.pix.height       = config.size.height;\n> > > -   arg->fmt.pix.pixelformat  = info.v4l2Format;\n> > > +   arg->fmt.pix.pixelformat  = info.v4l2Format[0];\n> >\n> > But for occasions like here, and in the CIO2\n> >       arg->fmt.pix.pixelformat  = info.v4l2Format.planar;\n> >\n> > Would be far more descriptive over which one is being chosen, and might\n> > help make it easier to spot issues when debugging multiplanar format\n> > bugs ...\n>\n> I agree. I don't like the names \"planar\" and \"multiplanar\" though, as\n> that's a bit ambiguous. V4L2 did a really bad job when it comes to\n> naming here. I'll try to think of better names after sleeping over it,\n> but please feel free to suggest alternatives :-) Bonus points of they're\n> short and have the same length :-)\n>\n> > >     arg->fmt.pix.field        = V4L2_FIELD_NONE;\n> > >     arg->fmt.pix.bytesperline = config.stride;\n> > >     arg->fmt.pix.sizeimage    = config.frameSize;\n> > >\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 2B807BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 10:54:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7A42D6916D;\n\tFri,  3 Sep 2021 12:54:01 +0200 (CEST)","from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com\n\t[IPv6:2a00:1450:4864:20::62b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3D3DE69167\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 12:54:00 +0200 (CEST)","by mail-ej1-x62b.google.com with SMTP id bt14so11260682ejb.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 03:54:00 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"d0DuKCK4\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=Nb/GhORTTig00c1TcNDo1yxnV95dbV0zQvPYg5EKqfk=;\n\tb=d0DuKCK4fh++FlCb1KSRoC0MpfUgeftzyEkFDyLsNH5cy8ilNyLT2JsTrpDRLXc3tQ\n\tLZ7fOxpbpJdFJY/pWCrgyHMahNMwqG9F8j2WlEZoXu1t4vi/9GsrTRXy6yxavF3tARSo\n\tFP/yikNwhD3mFXpcVvhtcx8tB0lz8PkUUcIFM=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=Nb/GhORTTig00c1TcNDo1yxnV95dbV0zQvPYg5EKqfk=;\n\tb=s4k+41kZiXhxjcgLmxk5yN5KJs8oBLGrlpropTMi8+mEj9UHNtJeAErx298vuTTDqS\n\tyTdx98W7en1Myh4mdU+Zm6OFMcHpEHl3S8XHjrMg12OJmzfRVoA3FU0gecVJi+9rhBiQ\n\t0MMGiLRnQyHEV6/BX5pjl1FzmsuTKtGTUN0cYfozAIU8bcUvsuqU8NlhFsbseH5SyX0T\n\twIFF8DmoyrenZnZSes+rMG3mf46axs6egSdoOc63K0IQTorKf2Jx8ZUDuN06x3NlXTEM\n\tkyDOtSyrclXPbx+Nn6uOf1NP4r3jN/I1RkuJGXUDVxYfxGGWIX93gXAnbIVY23AklTpM\n\t6uog==","X-Gm-Message-State":"AOAM533XESAgzWgkvpteUUSBxQS9bxzVDidn5OwNlHRxNrULeFJfCS4K\n\t99/yc65U34V18XIHMk89nXcqdpKMknhlL6akvcYAe0jh8GfdZw==","X-Google-Smtp-Source":"ABdhPJxqO5x/K7B76DjGDq17QjT+kdmmHElEUz6brOl/XovgcwJjVjJjdq2dszAAcHXLcfwx+XahazxChL2mc2CWpNk=","X-Received":"by 2002:a17:906:4fd6:: with SMTP id\n\ti22mr3569465ejw.92.1630666439613; \n\tFri, 03 Sep 2021 03:53:59 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-5-laurent.pinchart@ideasonboard.com>\n\t<797f3608-e856-3094-5e01-4c71c19cee0e@ideasonboard.com>\n\t<YTEZtNWvA059n8ty@pendragon.ideasonboard.com>","In-Reply-To":"<YTEZtNWvA059n8ty@pendragon.ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 19:53:48 +0900","Message-ID":"<CAO5uPHPBeoK7HDoL-uDWA51Afakebw17rX3zrarpywZ9MEqqew@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19349,"web_url":"https://patchwork.libcamera.org/comment/19349/","msgid":"<YTH//PVXb4z6rLIV@pendragon.ideasonboard.com>","date":"2021-09-03T10:59:08","subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Hiro,\n\nOn Fri, Sep 03, 2021 at 07:53:48PM +0900, Hirokazu Honda wrote:\n> On Fri, Sep 3, 2021 at 3:36 AM Laurent Pinchart wrote:\n> > On Thu, Sep 02, 2021 at 10:23:48AM +0100, Kieran Bingham wrote:\n> > > On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > > > V4L2 describes multi-planar formats with different 4CCs depending on\n> > > > whether or not the planes are stored contiguously in memory. Support\n> > > > this when translating between PixelFormat and V4L2PixelFormat.\n> > > >\n> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > ---\n> > > >  include/libcamera/internal/formats.h |   2 +-\n> > > >  src/libcamera/formats.cpp            | 141 ++++++++++++++++-----------\n> > > >  src/libcamera/pipeline/ipu3/cio2.cpp |   2 +-\n> > > >  src/libcamera/v4l2_pixelformat.cpp   |  11 ++-\n> > > >  src/v4l2/v4l2_camera_proxy.cpp       |   6 +-\n> > > >  5 files changed, 99 insertions(+), 63 deletions(-)\n> > > >\n> > > > diff --git a/include/libcamera/internal/formats.h b/include/libcamera/internal/formats.h\n> > > > index 8f314e99889b..f9ce5f0834fa 100644\n> > > > --- a/include/libcamera/internal/formats.h\n> > > > +++ b/include/libcamera/internal/formats.h\n> > > > @@ -55,7 +55,7 @@ public:\n> > > >     /* \\todo Add support for non-contiguous memory planes */\n> > > >     const char *name;\n> > > >     PixelFormat format;\n> > > > -   V4L2PixelFormat v4l2Format;\n> > > > +   std::array<V4L2PixelFormat, 2> v4l2Format;\n> > >\n> > > As discussed below, I'm curious if a struct would be clearer here and\n> > > help prevent bugs later...\n> > >\n> > >       struct {\n> > >               V4L2PixelFormat planar;\n> > >               V4L2PixelFormat multiplanar;\n> > >       } v4l2Format;\n> > >\n> > > >     unsigned int bitsPerPixel;\n> > > >     enum ColourEncoding colourEncoding;\n> > > >     bool packed;\n> > > > diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp\n> > > > index 76be93bc1c5c..ce7c5a2d267e 100644\n> > > > --- a/src/libcamera/formats.cpp\n> > > > +++ b/src/libcamera/formats.cpp\n> > > > @@ -56,7 +56,14 @@ LOG_DEFINE_CATEGORY(Formats)\n> > > >   * \\brief The PixelFormat described by this instance\n> > > >   *\n> > > >   * \\var PixelFormatInfo::v4l2Format\n> > > > - * \\brief The V4L2 pixel format corresponding to the PixelFormat\n> > > > + * \\brief The V4L2 pixel formats corresponding to the PixelFormat\n> > > > + *\n> > > > + * Multiple V4L2 formats may exist for one PixelFormat when the format uses\n> > > > + * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate\n> > > > + * planes formats. The two entries in the array store the contiguous and\n> > > > + * non-contiguous V4L2 formats respectively. If the PixelFormat isn't a\n> > > > + * multiplanar format, or if no corresponding non-contiguous V4L2 format\n> > > > + * exists, the second entry is invalid.\n> > > >   *\n> > > >   * \\var PixelFormatInfo::bitsPerPixel\n> > > >   * \\brief The average number of bits per pixel\n> > > > @@ -149,7 +156,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::RGB565, {\n> > > >             .name = \"RGB565\",\n> > > >             .format = formats::RGB565,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -159,7 +166,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::RGB565_BE, {\n> > > >             .name = \"RGB565_BE\",\n> > > >             .format = formats::RGB565_BE,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565X),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565X), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -169,7 +176,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::BGR888, {\n> > > >             .name = \"BGR888\",\n> > > >             .format = formats::BGR888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB24), },\n> > > >             .bitsPerPixel = 24,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -179,7 +186,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::RGB888, {\n> > > >             .name = \"RGB888\",\n> > > >             .format = formats::RGB888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGR24), },\n> > > >             .bitsPerPixel = 24,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -189,7 +196,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::XRGB8888, {\n> > > >             .name = \"XRGB8888\",\n> > > >             .format = formats::XRGB8888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XBGR32),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XBGR32), },\n> > > >             .bitsPerPixel = 32,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -199,7 +206,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::XBGR8888, {\n> > > >             .name = \"XBGR8888\",\n> > > >             .format = formats::XBGR8888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBX32),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBX32), },\n> > > >             .bitsPerPixel = 32,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -209,7 +216,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::BGRX8888, {\n> > > >             .name = \"BGRX8888\",\n> > > >             .format = formats::BGRX8888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XRGB32),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XRGB32), },\n> > > >             .bitsPerPixel = 32,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -219,7 +226,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::ABGR8888, {\n> > > >             .name = \"ABGR8888\",\n> > > >             .format = formats::ABGR8888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBA32), },\n> > > >             .bitsPerPixel = 32,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -229,7 +236,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::ARGB8888, {\n> > > >             .name = \"ARGB8888\",\n> > > >             .format = formats::ARGB8888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ABGR32), },\n> > > >             .bitsPerPixel = 32,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -239,7 +246,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::BGRA8888, {\n> > > >             .name = \"BGRA8888\",\n> > > >             .format = formats::BGRA8888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ARGB32), },\n> > > >             .bitsPerPixel = 32,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -249,7 +256,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::RGBA8888, {\n> > > >             .name = \"RGBA8888\",\n> > > >             .format = formats::RGBA8888,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGRA32), },\n> > > >             .bitsPerPixel = 32,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > >             .packed = false,\n> > > > @@ -261,7 +268,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::YUYV, {\n> > > >             .name = \"YUYV\",\n> > > >             .format = formats::YUYV,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YUYV), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -271,7 +278,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::YVYU, {\n> > > >             .name = \"YVYU\",\n> > > >             .format = formats::YVYU,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YVYU), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -281,7 +288,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::UYVY, {\n> > > >             .name = \"UYVY\",\n> > > >             .format = formats::UYVY,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_UYVY), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -291,7 +298,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::VYUY, {\n> > > >             .name = \"VYUY\",\n> > > >             .format = formats::VYUY,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_VYUY), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -303,7 +310,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::NV12, {\n> > > >             .name = \"NV12\",\n> > > >             .format = formats::NV12,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > > > +           .v4l2Format = {\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV12M),\n> > > > +           },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -313,7 +323,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::NV21, {\n> > > >             .name = \"NV21\",\n> > > >             .format = formats::NV21,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > > > +           .v4l2Format = {\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV21M),\n> > > > +           },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -323,7 +336,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::NV16, {\n> > > >             .name = \"NV16\",\n> > > >             .format = formats::NV16,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > > > +           .v4l2Format = {\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV16M),\n> > > > +           },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -333,7 +349,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::NV61, {\n> > > >             .name = \"NV61\",\n> > > >             .format = formats::NV61,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > > > +           .v4l2Format = {\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV61M),\n> > > > +           },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -343,7 +362,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::NV24, {\n> > > >             .name = \"NV24\",\n> > > >             .format = formats::NV24,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV24),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV24), },\n> > > >             .bitsPerPixel = 24,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -353,7 +372,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::NV42, {\n> > > >             .name = \"NV42\",\n> > > >             .format = formats::NV42,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV42),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV42), },\n> > > >             .bitsPerPixel = 24,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -363,7 +382,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::YUV420, {\n> > > >             .name = \"YUV420\",\n> > > >             .format = formats::YUV420,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > > > +           .v4l2Format = {\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV420M),\n> > > > +           },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -373,7 +395,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::YVU420, {\n> > > >             .name = \"YVU420\",\n> > > >             .format = formats::YVU420,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > > > +           .v4l2Format = {\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YVU420M),\n> > > > +           },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -383,7 +408,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::YUV422, {\n> > > >             .name = \"YUV422\",\n> > > >             .format = formats::YUV422,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > > > +           .v4l2Format = {\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV422M),\n> > > > +           },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -395,7 +423,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::R8, {\n> > > >             .name = \"R8\",\n> > > >             .format = formats::R8,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_GREY),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_GREY), },\n> > > >             .bitsPerPixel = 8,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -407,7 +435,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SBGGR8, {\n> > > >             .name = \"SBGGR8\",\n> > > >             .format = formats::SBGGR8,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), },\n> > > >             .bitsPerPixel = 8,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -417,7 +445,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGBRG8, {\n> > > >             .name = \"SGBRG8\",\n> > > >             .format = formats::SGBRG8,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), },\n> > > >             .bitsPerPixel = 8,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -427,7 +455,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGRBG8, {\n> > > >             .name = \"SGRBG8\",\n> > > >             .format = formats::SGRBG8,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), },\n> > > >             .bitsPerPixel = 8,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -437,7 +465,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SRGGB8, {\n> > > >             .name = \"SRGGB8\",\n> > > >             .format = formats::SRGGB8,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), },\n> > > >             .bitsPerPixel = 8,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -447,7 +475,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SBGGR10, {\n> > > >             .name = \"SBGGR10\",\n> > > >             .format = formats::SBGGR10,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -457,7 +485,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGBRG10, {\n> > > >             .name = \"SGBRG10\",\n> > > >             .format = formats::SGBRG10,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -467,7 +495,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGRBG10, {\n> > > >             .name = \"SGRBG10\",\n> > > >             .format = formats::SGRBG10,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -477,7 +505,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SRGGB10, {\n> > > >             .name = \"SRGGB10\",\n> > > >             .format = formats::SRGGB10,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -487,7 +515,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SBGGR10_CSI2P, {\n> > > >             .name = \"SBGGR10_CSI2P\",\n> > > >             .format = formats::SBGGR10_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -497,7 +525,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGBRG10_CSI2P, {\n> > > >             .name = \"SGBRG10_CSI2P\",\n> > > >             .format = formats::SGBRG10_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -507,7 +535,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGRBG10_CSI2P, {\n> > > >             .name = \"SGRBG10_CSI2P\",\n> > > >             .format = formats::SGRBG10_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -517,7 +545,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SRGGB10_CSI2P, {\n> > > >             .name = \"SRGGB10_CSI2P\",\n> > > >             .format = formats::SRGGB10_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -527,7 +555,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SBGGR12, {\n> > > >             .name = \"SBGGR12\",\n> > > >             .format = formats::SBGGR12,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -537,7 +565,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGBRG12, {\n> > > >             .name = \"SGBRG12\",\n> > > >             .format = formats::SGBRG12,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -547,7 +575,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGRBG12, {\n> > > >             .name = \"SGRBG12\",\n> > > >             .format = formats::SGRBG12,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -557,7 +585,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SRGGB12, {\n> > > >             .name = \"SRGGB12\",\n> > > >             .format = formats::SRGGB12,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -567,7 +595,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SBGGR12_CSI2P, {\n> > > >             .name = \"SBGGR12_CSI2P\",\n> > > >             .format = formats::SBGGR12_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -577,7 +605,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGBRG12_CSI2P, {\n> > > >             .name = \"SGBRG12_CSI2P\",\n> > > >             .format = formats::SGBRG12_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -587,7 +615,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGRBG12_CSI2P, {\n> > > >             .name = \"SGRBG12_CSI2P\",\n> > > >             .format = formats::SGRBG12_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -597,7 +625,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SRGGB12_CSI2P, {\n> > > >             .name = \"SRGGB12_CSI2P\",\n> > > >             .format = formats::SRGGB12_CSI2P,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), },\n> > > >             .bitsPerPixel = 12,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -607,7 +635,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SBGGR16, {\n> > > >             .name = \"SBGGR16\",\n> > > >             .format = formats::SBGGR16,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -617,7 +645,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGBRG16, {\n> > > >             .name = \"SGBRG16\",\n> > > >             .format = formats::SGBRG16,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -627,7 +655,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGRBG16, {\n> > > >             .name = \"SGRBG16\",\n> > > >             .format = formats::SGRBG16,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -637,7 +665,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SRGGB16, {\n> > > >             .name = \"SRGGB16\",\n> > > >             .format = formats::SRGGB16,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), },\n> > > >             .bitsPerPixel = 16,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = false,\n> > > > @@ -647,7 +675,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SBGGR10_IPU3, {\n> > > >             .name = \"SBGGR10_IPU3\",\n> > > >             .format = formats::SBGGR10_IPU3,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -658,7 +686,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGBRG10_IPU3, {\n> > > >             .name = \"SGBRG10_IPU3\",\n> > > >             .format = formats::SGBRG10_IPU3,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -668,7 +696,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SGRBG10_IPU3, {\n> > > >             .name = \"SGRBG10_IPU3\",\n> > > >             .format = formats::SGRBG10_IPU3,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -678,7 +706,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::SRGGB10_IPU3, {\n> > > >             .name = \"SRGGB10_IPU3\",\n> > > >             .format = formats::SRGGB10_IPU3,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10), },\n> > > >             .bitsPerPixel = 10,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > >             .packed = true,\n> > > > @@ -690,7 +718,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > >     { formats::MJPEG, {\n> > > >             .name = \"MJPEG\",\n> > > >             .format = formats::MJPEG,\n> > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),\n> > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_MJPEG), },\n> > > >             .bitsPerPixel = 0,\n> > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > >             .packed = false,\n> > > > @@ -736,7 +764,8 @@ const PixelFormatInfo &PixelFormatInfo::info(const V4L2PixelFormat &format)\n> > > >  {\n> > > >     const auto &info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),\n> > > >                                     [format](auto pair) {\n> > > > -                                           return pair.second.v4l2Format == format;\n> > > > +                                           return pair.second.v4l2Format[0] == format ||\n> > > > +                                                  pair.second.v4l2Format[1] == format;\n> > > >                                     });\n> > > >     if (info == pixelFormatInfo.end())\n> > > >             return pixelFormatInfoInvalid;\n> > > > diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > > index 9cedcb5b2879..5a9cffc80c8d 100644\n> > > > --- a/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > > +++ b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > > @@ -205,7 +205,7 @@ int CIO2Device::configure(const Size &size, V4L2DeviceFormat *outputFormat)\n> > > >\n> > > >     const PixelFormatInfo &info = PixelFormatInfo::info(itInfo->second);\n> > > >\n> > > > -   outputFormat->fourcc = info.v4l2Format;\n> > > > +   outputFormat->fourcc = info.v4l2Format[0];\n> \n> I probably miss something here. Why does selecting single planar\n> format always work here?\n\nBecause the CIO2 only supports Bayer formats, and those are all\nsingle-planar.\n\n> Ditto for v4l2_camera_proxy.cpp\n\nBecause the V4L2 compat layer doesn't support V4L2 multi-planar buffers\nyet.\n\nFor the CIO2 I think that's good enough, for the V4L2 compat layer we\nshould add full multi-planar support in the future.\n\n> > > >     outputFormat->size = sensorFormat.size;\n> > > >     outputFormat->planesCount = 1;\n> > > >\n> > > > diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp\n> > > > index 93fc4446cc64..93ead8928ed7 100644\n> > > > --- a/src/libcamera/v4l2_pixelformat.cpp\n> > > > +++ b/src/libcamera/v4l2_pixelformat.cpp\n> > > > @@ -67,12 +67,19 @@ const std::map<V4L2PixelFormat, PixelFormat> vpf2pf{\n> > > >\n> > > >     /* YUV planar formats. */\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV16), formats::NV16 },\n> > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV16M), formats::NV16 },\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV61), formats::NV61 },\n> > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV61M), formats::NV61 },\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV12), formats::NV12 },\n> > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV12M), formats::NV12 },\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV21), formats::NV21 },\n> > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV21M), formats::NV21 },\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_YUV420), formats::YUV420 },\n> > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YUV420M), formats::YUV420 },\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_YVU420), formats::YVU420 },\n> > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YVU420M), formats::YVU420 },\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_YUV422P), formats::YUV422 },\n> > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YUV422M), formats::YUV422 },\n> > >\n> > > I'm worried that we're losing information here.\n> > > But presumably it's handled by whether we use mplane or not on the\n> > > device...?\n> >\n> > We don't expose to applications whether the camera requires contiguous\n> > buffers or can support non-contiguous buffers. We don't otherwise lose\n> > information, as we pick a specific V4L2 format from the PixelFormat,\n> > based on an explicit selection of multiplanar or singleplanar V4L2\n> > format by the pipeline handler.\n> >\n> > > >     /* Greyscale formats. */\n> > > >     { V4L2PixelFormat(V4L2_PIX_FMT_GREY), formats::R8 },\n> > > > @@ -202,13 +209,13 @@ PixelFormat V4L2PixelFormat::toPixelFormat() const\n> > > >   * \\return The V4L2PixelFormat corresponding to \\a pixelFormat\n> > > >   */\n> > > >  V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat,\n> > > > -                                            [[maybe_unused]] bool multiplanar)\n> > > > +                                            bool multiplanar)\n> > > >  {\n> > > >     const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);\n> > > >     if (!info.isValid())\n> > > >             return V4L2PixelFormat();\n> > > >\n> > > > -   return info.v4l2Format;\n> > > > +   return info.v4l2Format[multiplanar ? 1 : 0];\n> > >\n> > > I was going to say, a struct with named fields of 'planar', and\n> > > 'multiplanar' might be a better, though I like the simplicity of this bit.\n> >\n> > Would be easy to rewrite though.\n> >\n> > > >  }\n> > > >\n> > > >  } /* namespace libcamera */\n> > > > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> > > > index 7682c4bddf90..8d8ee395954f 100644\n> > > > --- a/src/v4l2/v4l2_camera_proxy.cpp\n> > > > +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> > > > @@ -169,7 +169,7 @@ void V4L2CameraProxy::setFmtFromConfig(const StreamConfiguration &streamConfig)\n> > > >\n> > > >     v4l2PixFormat_.width        = size.width;\n> > > >     v4l2PixFormat_.height       = size.height;\n> > > > -   v4l2PixFormat_.pixelformat  = info.v4l2Format;\n> > > > +   v4l2PixFormat_.pixelformat  = info.v4l2Format[0];\n> > > >     v4l2PixFormat_.field        = V4L2_FIELD_NONE;\n> > > >     v4l2PixFormat_.bytesperline = streamConfig.stride;\n> > > >     v4l2PixFormat_.sizeimage    = streamConfig.frameSize;\n> > > > @@ -276,7 +276,7 @@ int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *file, struct v4l2_fmtdesc *\n> > > >     /* \\todo Add map from format to description. */\n> > > >     utils::strlcpy(reinterpret_cast<char *>(arg->description),\n> > > >                    \"Video Format Description\", sizeof(arg->description));\n> > > > -   arg->pixelformat = PixelFormatInfo::info(format).v4l2Format;\n> > > > +   arg->pixelformat = PixelFormatInfo::info(format).v4l2Format[0];\n> > > >\n> > > >     memset(arg->reserved, 0, sizeof(arg->reserved));\n> > > >\n> > > > @@ -315,7 +315,7 @@ int V4L2CameraProxy::tryFormat(struct v4l2_format *arg)\n> > > >\n> > > >     arg->fmt.pix.width        = config.size.width;\n> > > >     arg->fmt.pix.height       = config.size.height;\n> > > > -   arg->fmt.pix.pixelformat  = info.v4l2Format;\n> > > > +   arg->fmt.pix.pixelformat  = info.v4l2Format[0];\n> > >\n> > > But for occasions like here, and in the CIO2\n> > >       arg->fmt.pix.pixelformat  = info.v4l2Format.planar;\n> > >\n> > > Would be far more descriptive over which one is being chosen, and might\n> > > help make it easier to spot issues when debugging multiplanar format\n> > > bugs ...\n> >\n> > I agree. I don't like the names \"planar\" and \"multiplanar\" though, as\n> > that's a bit ambiguous. V4L2 did a really bad job when it comes to\n> > naming here. I'll try to think of better names after sleeping over it,\n> > but please feel free to suggest alternatives :-) Bonus points of they're\n> > short and have the same length :-)\n> >\n> > > >     arg->fmt.pix.field        = V4L2_FIELD_NONE;\n> > > >     arg->fmt.pix.bytesperline = config.stride;\n> > > >     arg->fmt.pix.sizeimage    = config.frameSize;\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 14A62BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 10:59:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7C7CC69167;\n\tFri,  3 Sep 2021 12:59: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 3A7FE69167\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 12:59:25 +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 9F839BBE;\n\tFri,  3 Sep 2021 12:59:24 +0200 (CEST)"],"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=\"WjVjeU0a\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630666764;\n\tbh=MfSPG/AEwO7ULeCw8D1u1CnpzBrUWJfjGc8ifYWNOvA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=WjVjeU0aJLUtHxhvc9ALyao4gDvqlZToA27ITG0a8NCAkaRBPCoNYrsGWEc+yzYom\n\tt/J+dwUxpO6hR78w9viJTrVoEb2kQgWRTrx++IilhPftSm0ztq/krF1igtQLtysg3V\n\tPAjBv8akzqUjhzbQaLDm+r5yr/whmllooI3VjOaU=","Date":"Fri, 3 Sep 2021 13:59:08 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Hirokazu Honda <hiroh@chromium.org>","Message-ID":"<YTH//PVXb4z6rLIV@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-5-laurent.pinchart@ideasonboard.com>\n\t<797f3608-e856-3094-5e01-4c71c19cee0e@ideasonboard.com>\n\t<YTEZtNWvA059n8ty@pendragon.ideasonboard.com>\n\t<CAO5uPHPBeoK7HDoL-uDWA51Afakebw17rX3zrarpywZ9MEqqew@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CAO5uPHPBeoK7HDoL-uDWA51Afakebw17rX3zrarpywZ9MEqqew@mail.gmail.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19352,"web_url":"https://patchwork.libcamera.org/comment/19352/","msgid":"<CAO5uPHMD-iCK3XwM+3iiAf8PoRB65WMwKknQw2A2FzPtOOeyCg@mail.gmail.com>","date":"2021-09-03T11:09:07","subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent,\n\nOn Fri, Sep 3, 2021 at 5:40 PM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Kieran,\n>\n> On Fri, Sep 03, 2021 at 09:24:02AM +0100, Kieran Bingham wrote:\n> > On 02/09/2021 19:07, Laurent Pinchart wrote:\n> > > On Thu, Sep 02, 2021 at 10:43:56AM +0100, Kieran Bingham wrote:\n> > >> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > >>> Multi-planar frame buffers can store their planes contiguously in\n> > >>> memory, or split them in discontiguous memory areas. Add a private\n> > >>> function to check in which of these two categories the frame buffer\n> > >>> belongs. This will be used to correctly handle the differences between\n> > >>> the V4L2 single and multi planar APIs.\n> > >>>\n> > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > >>> ---\n> > >>>  include/libcamera/internal/framebuffer.h |  2 ++\n> > >>>  src/libcamera/framebuffer.cpp            | 45 +++++++++++++++++++++++-\n> > >>>  2 files changed, 46 insertions(+), 1 deletion(-)\n> > >>>\n> > >>> diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h\n> > >>> index 606aed2b4782..cd33c295466e 100644\n> > >>> --- a/include/libcamera/internal/framebuffer.h\n> > >>> +++ b/include/libcamera/internal/framebuffer.h\n> > >>> @@ -21,9 +21,11 @@ public:\n> > >>>   Private();\n> > >>>\n> > >>>   void setRequest(Request *request) { request_ = request; }\n> > >>> + bool isContiguous() const { return isContiguous_; }\n> > >>>\n> > >>>  private:\n> > >>>   Request *request_;\n> > >>> + bool isContiguous_;\n> > >>>  };\n> > >>>\n> > >>>  } /* namespace libcamera */\n> > >>> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > >>> index 53ef89bf458f..99265b44da43 100644\n> > >>> --- a/src/libcamera/framebuffer.cpp\n> > >>> +++ b/src/libcamera/framebuffer.cpp\n> > >>> @@ -106,7 +106,7 @@ LOG_DEFINE_CATEGORY(Buffer)\n> > >>>   */\n> > >>>\n> > >>>  FrameBuffer::Private::Private()\n> > >>> - : request_(nullptr)\n> > >>> + : request_(nullptr), isContiguous_(true)\n> > >>>  {\n> > >>>  }\n> > >>>\n> > >>> @@ -120,6 +120,17 @@ FrameBuffer::Private::Private()\n> > >>>   * handlers, it is called by the pipeline handlers themselves.\n> > >>>   */\n> > >>>\n> > >>> +/**\n> > >>> + * \\fn FrameBuffer::Private::isContiguous()\n> > >>> + * \\brief Check if the frame buffer stores planes contiguously in memory\n> > >>> + *\n> > >>> + * Multi-planar frame buffers can store their planes contiguously in memory, or\n> > >>> + * split them in discontiguous memory areas. This function checks in which of\n> > >>\n> > >> 'split them into discontiguous'\n> > >>\n> > >>> + * these two categories the frame buffer belongs.\n> > >>> + *\n> > >>> + * \\return True if the planes are stored contiguously in memory, false otherwise\n> > >>> + */\n> > >>> +\n> > >>>  /**\n> > >>>   * \\class FrameBuffer\n> > >>>   * \\brief Frame buffer data and its associated dynamic metadata\n> > >>> @@ -199,9 +210,41 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> > >>>   : Extensible(std::make_unique<Private>()), planes_(planes),\n> > >>>     cookie_(cookie)\n> > >>>  {\n> > >>> + unsigned int offset = 0;\n> > >>> +\n> > >>>   /* \\todo Remove the assertions after sufficient testing */\n> > >>>   for (const auto &plane : planes_)\n> > >>>           ASSERT(plane.offset != Plane::kInvalidOffset);\n\nnit:\nShall we merge the two for loops?\nAt least, I would move offset to before isContiguous.\n\n> > >>> +\n> > >>> + bool isContiguous = true;\n> > >>> + ino_t inode = 0;\n> > >>> +\n> > >>> + for (const auto &plane : planes_) {\n> > >>> +         if (plane.offset != offset) {\n> > >>> +                 isContiguous = false;\n> > >>> +                 break;\n> > >>> +         }\n> > >>> +\n> > >>> +         /*\n> > >>> +          * Two different dmabuf file descriptors may still refer to the\n> > >>> +          * same dmabuf instance. Check this using inodes.\n> > >>\n> > >> Going back to the FileDescriptor::inode() extension, if that cached the\n> > >> inode (it can't change) on either first call, or construction, couldn't\n> > >> this whole check be simplified to:\n> > >>\n> > >>            if (plane.fd.inode() != planes_[0].fd.inode()) {\n> > >>                    isContiguous = false;\n> > >>                    break;\n> > >>            }\n> > >\n> > > This would call fstat() every time, while the fd comparison here is\n> > > meant to skip the fstat() calls in case the numerical fd match. We could\n> > > do\n> >\n> > Did you miss the part where I said \"If we cache the inode in\n> > FileDescriptor\"?\n>\n> No I didn't :-)\n>\n> > My intention was that caching there would mean we can simplify\n> > comparisons here, without incurring repeated fstat calls.\n>\n> If the numerical fds are identical, there's no need to compare the\n> inodes, and we can avoid calling fstat() at all.\n>\n> > >             if (plane.fd != planes_[0].fd)\n> > >\n> > > if we introduced an operator==(), but as explained in a reply to the\n> > > patch that adds inode(), I'd like to handle that later, to avoid\n> > > blocking this series.\n> >\n> > Ok, I still don't see the harm in caching the inode within\n> > FileDescriptor(), it can't change once read the first time.\n>\n> What we'd need to do in operator==() is to first compare the numerical\n> fds, and only compare the inodes if the fds differ. Technically that's\n> not an issue, but it requires deciding what the semantics of equality is\n> as explaining in a separate e-mail, and that's the part that I'd like to\n> defer to avoid delaying the offset fixes.\n>\n> > But, it can be deferred:\n> >\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >\n> > >>> +          */\n> > >>> +         if (plane.fd.fd() != planes_[0].fd.fd()) {\n> > >>> +                 if (!inode)\n> > >>> +                         inode = planes_[0].fd.inode();\n\nShall error case (i.e plane.fd.inode() == 0) be handled?\n\n-Hiro\n> > >>> +                 if (plane.fd.inode() != inode) {\n> > >>> +                         isContiguous = false;\n> > >>> +                         break;\n> > >>> +                 }\n> > >>> +         }\n> > >>> +\n> > >>> +         offset += plane.length;\n> > >>> + }\n> > >>> +\n> > >>> + LOG(Buffer, Debug)\n> > >>> +         << \"Buffer is \" << (isContiguous ? \"not \" : \"\") << \"contiguous\";\n> > >>> +\n> > >>> + _d()->isContiguous_ = isContiguous;\n> > >>>  }\n> > >>>\n> > >>>  /**\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 1E413BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 11:09:22 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F06B16916B;\n\tFri,  3 Sep 2021 13:09:21 +0200 (CEST)","from mail-yb1-xb35.google.com (mail-yb1-xb35.google.com\n\t[IPv6:2607:f8b0:4864:20::b35])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D406369168\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 13:09:19 +0200 (CEST)","by mail-yb1-xb35.google.com with SMTP id c6so9385454ybm.10\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 04:09:19 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"ixQ+DH0l\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=QKP3vJgMQOS9E2GedZLFdBk26UyfmemG4Sk08tvyvxs=;\n\tb=ixQ+DH0llObv/srpZC1G9UASZjYFqhGnltxDYh3EI+xl68HgG/0vWIAu/CY/ICIhJm\n\tDYaGnXAMbWi4erHSWtgALa1uiMQq1+wdgmYME3dy/cSXHBxpQod1FfhIl60G3+/nCYhx\n\tinDgNar3DDcVXM/MC/4DUhq3Uf8LKamOU2q4Q=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=QKP3vJgMQOS9E2GedZLFdBk26UyfmemG4Sk08tvyvxs=;\n\tb=jUIZoxjWPAfJBZ0CLg0HPzA0JatF4IF9w8iEBXgrX8nR45RJSZO+zkwuI6c84YVYhu\n\teB/Lu+eQCvyh7sYsMZp/zUwFUz4jUbH8XW8UyK04WfhmmhKMkE2wgJj7i9CEKzHIPCkf\n\tk3zOeYoTFT3do5d1V9WdsLVFIBCu/0pEWBcFH3u4pJThhOELfsyt/D1o8+iI6gqPdshk\n\tVtVDOynuU2wdx6q7iJewWIcMLVEGvFVrSKT45VG6zstlTf5JRK1Nx39enDCv01N6W6Fd\n\tsN3W8i5wtDXxhEBD2BJ+m24N66qv27E7qkorx1lTTgSyHOhfvoMM7DzUAA/6AkoTtKxE\n\t57Hw==","X-Gm-Message-State":"AOAM5315i0RgtPwZdw7AXQ4eYAagxp8HhoHL11sWm3DpNGs7L4EJRToN\n\tU7j+rrkf5wUAjPGnhdHCUWvn+ENlvTBslBFIBeZE6g==","X-Google-Smtp-Source":"ABdhPJxYnzJ5OVXSMtGSSj/V0Rtwue1AzWiHTZzeQa8y/upgVcl+cdJUH4z9nMmalCYluwF+I/b+GDO4dQSirkZqn+w=","X-Received":"by 2002:a25:824e:: with SMTP id\n\td14mr4153923ybn.179.1630667358751; \n\tFri, 03 Sep 2021 04:09:18 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>\n\t<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>\n\t<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>\n\t<ed14c57f-8b16-f2a0-a13f-fcfd4069897b@ideasonboard.com>\n\t<YTHfUK4YUZ9AMx2V@pendragon.ideasonboard.com>","In-Reply-To":"<YTHfUK4YUZ9AMx2V@pendragon.ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 20:09:07 +0900","Message-ID":"<CAO5uPHMD-iCK3XwM+3iiAf8PoRB65WMwKknQw2A2FzPtOOeyCg@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19353,"web_url":"https://patchwork.libcamera.org/comment/19353/","msgid":"<CAO5uPHMeQC5HQ_CFs_Qt2MWrfKrk5X-sjVLj_yyVkAJd6Aq2Ug@mail.gmail.com>","date":"2021-09-03T11:11:04","subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent,\n\nOn Fri, Sep 3, 2021 at 7:59 PM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Hiro,\n>\n> On Fri, Sep 03, 2021 at 07:53:48PM +0900, Hirokazu Honda wrote:\n> > On Fri, Sep 3, 2021 at 3:36 AM Laurent Pinchart wrote:\n> > > On Thu, Sep 02, 2021 at 10:23:48AM +0100, Kieran Bingham wrote:\n> > > > On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > > > > V4L2 describes multi-planar formats with different 4CCs depending on\n> > > > > whether or not the planes are stored contiguously in memory. Support\n> > > > > this when translating between PixelFormat and V4L2PixelFormat.\n> > > > >\n> > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > ---\n> > > > >  include/libcamera/internal/formats.h |   2 +-\n> > > > >  src/libcamera/formats.cpp            | 141 ++++++++++++++++-----------\n> > > > >  src/libcamera/pipeline/ipu3/cio2.cpp |   2 +-\n> > > > >  src/libcamera/v4l2_pixelformat.cpp   |  11 ++-\n> > > > >  src/v4l2/v4l2_camera_proxy.cpp       |   6 +-\n> > > > >  5 files changed, 99 insertions(+), 63 deletions(-)\n> > > > >\n> > > > > diff --git a/include/libcamera/internal/formats.h b/include/libcamera/internal/formats.h\n> > > > > index 8f314e99889b..f9ce5f0834fa 100644\n> > > > > --- a/include/libcamera/internal/formats.h\n> > > > > +++ b/include/libcamera/internal/formats.h\n> > > > > @@ -55,7 +55,7 @@ public:\n> > > > >     /* \\todo Add support for non-contiguous memory planes */\n> > > > >     const char *name;\n> > > > >     PixelFormat format;\n> > > > > -   V4L2PixelFormat v4l2Format;\n> > > > > +   std::array<V4L2PixelFormat, 2> v4l2Format;\n> > > >\n> > > > As discussed below, I'm curious if a struct would be clearer here and\n> > > > help prevent bugs later...\n> > > >\n> > > >       struct {\n> > > >               V4L2PixelFormat planar;\n> > > >               V4L2PixelFormat multiplanar;\n> > > >       } v4l2Format;\n> > > >\n> > > > >     unsigned int bitsPerPixel;\n> > > > >     enum ColourEncoding colourEncoding;\n> > > > >     bool packed;\n> > > > > diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp\n> > > > > index 76be93bc1c5c..ce7c5a2d267e 100644\n> > > > > --- a/src/libcamera/formats.cpp\n> > > > > +++ b/src/libcamera/formats.cpp\n> > > > > @@ -56,7 +56,14 @@ LOG_DEFINE_CATEGORY(Formats)\n> > > > >   * \\brief The PixelFormat described by this instance\n> > > > >   *\n> > > > >   * \\var PixelFormatInfo::v4l2Format\n> > > > > - * \\brief The V4L2 pixel format corresponding to the PixelFormat\n> > > > > + * \\brief The V4L2 pixel formats corresponding to the PixelFormat\n> > > > > + *\n> > > > > + * Multiple V4L2 formats may exist for one PixelFormat when the format uses\n> > > > > + * multiple planes, as V4L2 defines separate 4CCs for contiguous and separate\n> > > > > + * planes formats. The two entries in the array store the contiguous and\n> > > > > + * non-contiguous V4L2 formats respectively. If the PixelFormat isn't a\n> > > > > + * multiplanar format, or if no corresponding non-contiguous V4L2 format\n> > > > > + * exists, the second entry is invalid.\n> > > > >   *\n> > > > >   * \\var PixelFormatInfo::bitsPerPixel\n> > > > >   * \\brief The average number of bits per pixel\n> > > > > @@ -149,7 +156,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::RGB565, {\n> > > > >             .name = \"RGB565\",\n> > > > >             .format = formats::RGB565,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -159,7 +166,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::RGB565_BE, {\n> > > > >             .name = \"RGB565_BE\",\n> > > > >             .format = formats::RGB565_BE,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB565X),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB565X), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -169,7 +176,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::BGR888, {\n> > > > >             .name = \"BGR888\",\n> > > > >             .format = formats::BGR888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGB24), },\n> > > > >             .bitsPerPixel = 24,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -179,7 +186,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::RGB888, {\n> > > > >             .name = \"RGB888\",\n> > > > >             .format = formats::RGB888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGR24), },\n> > > > >             .bitsPerPixel = 24,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -189,7 +196,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::XRGB8888, {\n> > > > >             .name = \"XRGB8888\",\n> > > > >             .format = formats::XRGB8888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XBGR32),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XBGR32), },\n> > > > >             .bitsPerPixel = 32,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -199,7 +206,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::XBGR8888, {\n> > > > >             .name = \"XBGR8888\",\n> > > > >             .format = formats::XBGR8888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBX32),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBX32), },\n> > > > >             .bitsPerPixel = 32,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -209,7 +216,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::BGRX8888, {\n> > > > >             .name = \"BGRX8888\",\n> > > > >             .format = formats::BGRX8888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_XRGB32),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_XRGB32), },\n> > > > >             .bitsPerPixel = 32,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -219,7 +226,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::ABGR8888, {\n> > > > >             .name = \"ABGR8888\",\n> > > > >             .format = formats::ABGR8888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_RGBA32), },\n> > > > >             .bitsPerPixel = 32,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -229,7 +236,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::ARGB8888, {\n> > > > >             .name = \"ARGB8888\",\n> > > > >             .format = formats::ARGB8888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ABGR32), },\n> > > > >             .bitsPerPixel = 32,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -239,7 +246,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::BGRA8888, {\n> > > > >             .name = \"BGRA8888\",\n> > > > >             .format = formats::BGRA8888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_ARGB32), },\n> > > > >             .bitsPerPixel = 32,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -249,7 +256,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::RGBA8888, {\n> > > > >             .name = \"RGBA8888\",\n> > > > >             .format = formats::RGBA8888,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_BGRA32), },\n> > > > >             .bitsPerPixel = 32,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRGB,\n> > > > >             .packed = false,\n> > > > > @@ -261,7 +268,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::YUYV, {\n> > > > >             .name = \"YUYV\",\n> > > > >             .format = formats::YUYV,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YUYV), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -271,7 +278,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::YVYU, {\n> > > > >             .name = \"YVYU\",\n> > > > >             .format = formats::YVYU,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_YVYU), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -281,7 +288,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::UYVY, {\n> > > > >             .name = \"UYVY\",\n> > > > >             .format = formats::UYVY,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_UYVY), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -291,7 +298,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::VYUY, {\n> > > > >             .name = \"VYUY\",\n> > > > >             .format = formats::VYUY,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_VYUY), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -303,7 +310,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::NV12, {\n> > > > >             .name = \"NV12\",\n> > > > >             .format = formats::NV12,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > > > > +           .v4l2Format = {\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV12),\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV12M),\n> > > > > +           },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -313,7 +323,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::NV21, {\n> > > > >             .name = \"NV21\",\n> > > > >             .format = formats::NV21,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > > > > +           .v4l2Format = {\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV21),\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV21M),\n> > > > > +           },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -323,7 +336,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::NV16, {\n> > > > >             .name = \"NV16\",\n> > > > >             .format = formats::NV16,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > > > > +           .v4l2Format = {\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV16),\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV16M),\n> > > > > +           },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -333,7 +349,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::NV61, {\n> > > > >             .name = \"NV61\",\n> > > > >             .format = formats::NV61,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > > > > +           .v4l2Format = {\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV61),\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_NV61M),\n> > > > > +           },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -343,7 +362,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::NV24, {\n> > > > >             .name = \"NV24\",\n> > > > >             .format = formats::NV24,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV24),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV24), },\n> > > > >             .bitsPerPixel = 24,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -353,7 +372,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::NV42, {\n> > > > >             .name = \"NV42\",\n> > > > >             .format = formats::NV42,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV42),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_NV42), },\n> > > > >             .bitsPerPixel = 24,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -363,7 +382,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::YUV420, {\n> > > > >             .name = \"YUV420\",\n> > > > >             .format = formats::YUV420,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > > > > +           .v4l2Format = {\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV420),\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV420M),\n> > > > > +           },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -373,7 +395,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::YVU420, {\n> > > > >             .name = \"YVU420\",\n> > > > >             .format = formats::YVU420,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > > > > +           .v4l2Format = {\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YVU420),\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YVU420M),\n> > > > > +           },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -383,7 +408,10 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::YUV422, {\n> > > > >             .name = \"YUV422\",\n> > > > >             .format = formats::YUV422,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > > > > +           .v4l2Format = {\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),\n> > > > > +                   V4L2PixelFormat(V4L2_PIX_FMT_YUV422M),\n> > > > > +           },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -395,7 +423,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::R8, {\n> > > > >             .name = \"R8\",\n> > > > >             .format = formats::R8,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_GREY),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_GREY), },\n> > > > >             .bitsPerPixel = 8,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -407,7 +435,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SBGGR8, {\n> > > > >             .name = \"SBGGR8\",\n> > > > >             .format = formats::SBGGR8,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8), },\n> > > > >             .bitsPerPixel = 8,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -417,7 +445,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGBRG8, {\n> > > > >             .name = \"SGBRG8\",\n> > > > >             .format = formats::SGBRG8,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8), },\n> > > > >             .bitsPerPixel = 8,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -427,7 +455,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGRBG8, {\n> > > > >             .name = \"SGRBG8\",\n> > > > >             .format = formats::SGRBG8,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8), },\n> > > > >             .bitsPerPixel = 8,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -437,7 +465,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SRGGB8, {\n> > > > >             .name = \"SRGGB8\",\n> > > > >             .format = formats::SRGGB8,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8), },\n> > > > >             .bitsPerPixel = 8,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -447,7 +475,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SBGGR10, {\n> > > > >             .name = \"SBGGR10\",\n> > > > >             .format = formats::SBGGR10,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -457,7 +485,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGBRG10, {\n> > > > >             .name = \"SGBRG10\",\n> > > > >             .format = formats::SGBRG10,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -467,7 +495,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGRBG10, {\n> > > > >             .name = \"SGRBG10\",\n> > > > >             .format = formats::SGRBG10,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -477,7 +505,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SRGGB10, {\n> > > > >             .name = \"SRGGB10\",\n> > > > >             .format = formats::SRGGB10,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -487,7 +515,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SBGGR10_CSI2P, {\n> > > > >             .name = \"SBGGR10_CSI2P\",\n> > > > >             .format = formats::SBGGR10_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -497,7 +525,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGBRG10_CSI2P, {\n> > > > >             .name = \"SGBRG10_CSI2P\",\n> > > > >             .format = formats::SGBRG10_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -507,7 +535,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGRBG10_CSI2P, {\n> > > > >             .name = \"SGRBG10_CSI2P\",\n> > > > >             .format = formats::SGRBG10_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -517,7 +545,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SRGGB10_CSI2P, {\n> > > > >             .name = \"SRGGB10_CSI2P\",\n> > > > >             .format = formats::SRGGB10_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -527,7 +555,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SBGGR12, {\n> > > > >             .name = \"SBGGR12\",\n> > > > >             .format = formats::SBGGR12,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -537,7 +565,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGBRG12, {\n> > > > >             .name = \"SGBRG12\",\n> > > > >             .format = formats::SGBRG12,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -547,7 +575,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGRBG12, {\n> > > > >             .name = \"SGRBG12\",\n> > > > >             .format = formats::SGRBG12,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -557,7 +585,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SRGGB12, {\n> > > > >             .name = \"SRGGB12\",\n> > > > >             .format = formats::SRGGB12,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -567,7 +595,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SBGGR12_CSI2P, {\n> > > > >             .name = \"SBGGR12_CSI2P\",\n> > > > >             .format = formats::SBGGR12_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -577,7 +605,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGBRG12_CSI2P, {\n> > > > >             .name = \"SGBRG12_CSI2P\",\n> > > > >             .format = formats::SGBRG12_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -587,7 +615,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGRBG12_CSI2P, {\n> > > > >             .name = \"SGRBG12_CSI2P\",\n> > > > >             .format = formats::SGRBG12_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -597,7 +625,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SRGGB12_CSI2P, {\n> > > > >             .name = \"SRGGB12_CSI2P\",\n> > > > >             .format = formats::SRGGB12_CSI2P,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P), },\n> > > > >             .bitsPerPixel = 12,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -607,7 +635,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SBGGR16, {\n> > > > >             .name = \"SBGGR16\",\n> > > > >             .format = formats::SBGGR16,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -617,7 +645,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGBRG16, {\n> > > > >             .name = \"SGBRG16\",\n> > > > >             .format = formats::SGBRG16,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -627,7 +655,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGRBG16, {\n> > > > >             .name = \"SGRBG16\",\n> > > > >             .format = formats::SGRBG16,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -637,7 +665,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SRGGB16, {\n> > > > >             .name = \"SRGGB16\",\n> > > > >             .format = formats::SRGGB16,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16), },\n> > > > >             .bitsPerPixel = 16,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = false,\n> > > > > @@ -647,7 +675,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SBGGR10_IPU3, {\n> > > > >             .name = \"SBGGR10_IPU3\",\n> > > > >             .format = formats::SBGGR10_IPU3,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -658,7 +686,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGBRG10_IPU3, {\n> > > > >             .name = \"SGBRG10_IPU3\",\n> > > > >             .format = formats::SGBRG10_IPU3,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -668,7 +696,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SGRBG10_IPU3, {\n> > > > >             .name = \"SGRBG10_IPU3\",\n> > > > >             .format = formats::SGRBG10_IPU3,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -678,7 +706,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::SRGGB10_IPU3, {\n> > > > >             .name = \"SRGGB10_IPU3\",\n> > > > >             .format = formats::SRGGB10_IPU3,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10), },\n> > > > >             .bitsPerPixel = 10,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingRAW,\n> > > > >             .packed = true,\n> > > > > @@ -690,7 +718,7 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{\n> > > > >     { formats::MJPEG, {\n> > > > >             .name = \"MJPEG\",\n> > > > >             .format = formats::MJPEG,\n> > > > > -           .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),\n> > > > > +           .v4l2Format = { V4L2PixelFormat(V4L2_PIX_FMT_MJPEG), },\n> > > > >             .bitsPerPixel = 0,\n> > > > >             .colourEncoding = PixelFormatInfo::ColourEncodingYUV,\n> > > > >             .packed = false,\n> > > > > @@ -736,7 +764,8 @@ const PixelFormatInfo &PixelFormatInfo::info(const V4L2PixelFormat &format)\n> > > > >  {\n> > > > >     const auto &info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),\n> > > > >                                     [format](auto pair) {\n> > > > > -                                           return pair.second.v4l2Format == format;\n> > > > > +                                           return pair.second.v4l2Format[0] == format ||\n> > > > > +                                                  pair.second.v4l2Format[1] == format;\n> > > > >                                     });\n> > > > >     if (info == pixelFormatInfo.end())\n> > > > >             return pixelFormatInfoInvalid;\n> > > > > diff --git a/src/libcamera/pipeline/ipu3/cio2.cpp b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > > > index 9cedcb5b2879..5a9cffc80c8d 100644\n> > > > > --- a/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > > > +++ b/src/libcamera/pipeline/ipu3/cio2.cpp\n> > > > > @@ -205,7 +205,7 @@ int CIO2Device::configure(const Size &size, V4L2DeviceFormat *outputFormat)\n> > > > >\n> > > > >     const PixelFormatInfo &info = PixelFormatInfo::info(itInfo->second);\n> > > > >\n> > > > > -   outputFormat->fourcc = info.v4l2Format;\n> > > > > +   outputFormat->fourcc = info.v4l2Format[0];\n> >\n> > I probably miss something here. Why does selecting single planar\n> > format always work here?\n>\n> Because the CIO2 only supports Bayer formats, and those are all\n> single-planar.\n>\n> > Ditto for v4l2_camera_proxy.cpp\n>\n> Because the V4L2 compat layer doesn't support V4L2 multi-planar buffers\n> yet.\n>\n> For the CIO2 I think that's good enough, for the V4L2 compat layer we\n> should add full multi-planar support in the future.\n\nThanks for explaining.\nPerhaps should these comments be added?\n\nReviewed-by: Hirokazu Honda <hiroh@chromium.org>\n\n-Hiro\n>\n> > > > >     outputFormat->size = sensorFormat.size;\n> > > > >     outputFormat->planesCount = 1;\n> > > > >\n> > > > > diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp\n> > > > > index 93fc4446cc64..93ead8928ed7 100644\n> > > > > --- a/src/libcamera/v4l2_pixelformat.cpp\n> > > > > +++ b/src/libcamera/v4l2_pixelformat.cpp\n> > > > > @@ -67,12 +67,19 @@ const std::map<V4L2PixelFormat, PixelFormat> vpf2pf{\n> > > > >\n> > > > >     /* YUV planar formats. */\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV16), formats::NV16 },\n> > > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV16M), formats::NV16 },\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV61), formats::NV61 },\n> > > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV61M), formats::NV61 },\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV12), formats::NV12 },\n> > > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV12M), formats::NV12 },\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_NV21), formats::NV21 },\n> > > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_NV21M), formats::NV21 },\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_YUV420), formats::YUV420 },\n> > > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YUV420M), formats::YUV420 },\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_YVU420), formats::YVU420 },\n> > > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YVU420M), formats::YVU420 },\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_YUV422P), formats::YUV422 },\n> > > > > +   { V4L2PixelFormat(V4L2_PIX_FMT_YUV422M), formats::YUV422 },\n> > > >\n> > > > I'm worried that we're losing information here.\n> > > > But presumably it's handled by whether we use mplane or not on the\n> > > > device...?\n> > >\n> > > We don't expose to applications whether the camera requires contiguous\n> > > buffers or can support non-contiguous buffers. We don't otherwise lose\n> > > information, as we pick a specific V4L2 format from the PixelFormat,\n> > > based on an explicit selection of multiplanar or singleplanar V4L2\n> > > format by the pipeline handler.\n> > >\n> > > > >     /* Greyscale formats. */\n> > > > >     { V4L2PixelFormat(V4L2_PIX_FMT_GREY), formats::R8 },\n> > > > > @@ -202,13 +209,13 @@ PixelFormat V4L2PixelFormat::toPixelFormat() const\n> > > > >   * \\return The V4L2PixelFormat corresponding to \\a pixelFormat\n> > > > >   */\n> > > > >  V4L2PixelFormat V4L2PixelFormat::fromPixelFormat(const PixelFormat &pixelFormat,\n> > > > > -                                            [[maybe_unused]] bool multiplanar)\n> > > > > +                                            bool multiplanar)\n> > > > >  {\n> > > > >     const PixelFormatInfo &info = PixelFormatInfo::info(pixelFormat);\n> > > > >     if (!info.isValid())\n> > > > >             return V4L2PixelFormat();\n> > > > >\n> > > > > -   return info.v4l2Format;\n> > > > > +   return info.v4l2Format[multiplanar ? 1 : 0];\n> > > >\n> > > > I was going to say, a struct with named fields of 'planar', and\n> > > > 'multiplanar' might be a better, though I like the simplicity of this bit.\n> > >\n> > > Would be easy to rewrite though.\n> > >\n> > > > >  }\n> > > > >\n> > > > >  } /* namespace libcamera */\n> > > > > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> > > > > index 7682c4bddf90..8d8ee395954f 100644\n> > > > > --- a/src/v4l2/v4l2_camera_proxy.cpp\n> > > > > +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> > > > > @@ -169,7 +169,7 @@ void V4L2CameraProxy::setFmtFromConfig(const StreamConfiguration &streamConfig)\n> > > > >\n> > > > >     v4l2PixFormat_.width        = size.width;\n> > > > >     v4l2PixFormat_.height       = size.height;\n> > > > > -   v4l2PixFormat_.pixelformat  = info.v4l2Format;\n> > > > > +   v4l2PixFormat_.pixelformat  = info.v4l2Format[0];\n> > > > >     v4l2PixFormat_.field        = V4L2_FIELD_NONE;\n> > > > >     v4l2PixFormat_.bytesperline = streamConfig.stride;\n> > > > >     v4l2PixFormat_.sizeimage    = streamConfig.frameSize;\n> > > > > @@ -276,7 +276,7 @@ int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *file, struct v4l2_fmtdesc *\n> > > > >     /* \\todo Add map from format to description. */\n> > > > >     utils::strlcpy(reinterpret_cast<char *>(arg->description),\n> > > > >                    \"Video Format Description\", sizeof(arg->description));\n> > > > > -   arg->pixelformat = PixelFormatInfo::info(format).v4l2Format;\n> > > > > +   arg->pixelformat = PixelFormatInfo::info(format).v4l2Format[0];\n> > > > >\n> > > > >     memset(arg->reserved, 0, sizeof(arg->reserved));\n> > > > >\n> > > > > @@ -315,7 +315,7 @@ int V4L2CameraProxy::tryFormat(struct v4l2_format *arg)\n> > > > >\n> > > > >     arg->fmt.pix.width        = config.size.width;\n> > > > >     arg->fmt.pix.height       = config.size.height;\n> > > > > -   arg->fmt.pix.pixelformat  = info.v4l2Format;\n> > > > > +   arg->fmt.pix.pixelformat  = info.v4l2Format[0];\n> > > >\n> > > > But for occasions like here, and in the CIO2\n> > > >       arg->fmt.pix.pixelformat  = info.v4l2Format.planar;\n> > > >\n> > > > Would be far more descriptive over which one is being chosen, and might\n> > > > help make it easier to spot issues when debugging multiplanar format\n> > > > bugs ...\n> > >\n> > > I agree. I don't like the names \"planar\" and \"multiplanar\" though, as\n> > > that's a bit ambiguous. V4L2 did a really bad job when it comes to\n> > > naming here. I'll try to think of better names after sleeping over it,\n> > > but please feel free to suggest alternatives :-) Bonus points of they're\n> > > short and have the same length :-)\n> > >\n> > > > >     arg->fmt.pix.field        = V4L2_FIELD_NONE;\n> > > > >     arg->fmt.pix.bytesperline = config.stride;\n> > > > >     arg->fmt.pix.sizeimage    = config.frameSize;\n> > > > >\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 1BF44BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 11:11:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8F2EA6916B;\n\tFri,  3 Sep 2021 13:11:18 +0200 (CEST)","from mail-yb1-xb2c.google.com (mail-yb1-xb2c.google.com\n\t[IPv6:2607:f8b0:4864:20::b2c])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2937069168\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 13:11:17 +0200 (CEST)","by mail-yb1-xb2c.google.com with SMTP id j195so1441406ybg.6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 04:11:17 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"SxHVAUod\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=9qnx6BQHGzEt0Z8axSQzg6tqguk/JoFt5Qu6GSKCoAI=;\n\tb=SxHVAUod1/9Pj7f8aFgivxaPvRGgc/rGof9PnKuWdVEpW0PV35+EMm2buSmGbIvgZR\n\tHbyO8jGQfngUoK6Iqx4dUa2ebL354GzNFyNphdMTRvjttP3aEvvjoBwKzDqj9IfTqUZB\n\timIriZkjvTebaqkiLTaGNs2Wvwf3yXkamR9Fw=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=9qnx6BQHGzEt0Z8axSQzg6tqguk/JoFt5Qu6GSKCoAI=;\n\tb=qT59b8mXdro77wSp1zZWRUzjYH6gE1AI08Sq1wnHvx+xlH6TUo/Uid5gX58i7noh1G\n\t0jnmxAY9H4Tie/Fx2S/dA3I18Z9XsHZkG/CfHGOvM8FUi0cmCA25KUIaPVO1qECfHxkR\n\tm8pEZhPGb6b4HbCZDQWJgylWiOGaSMiyK2BzOXlAclFuxtkOqZX6KdZiSfSyG//h3FxV\n\t8z6iWRnAp9fjt1WBf2cXsh56cUdPGtVtHg6Wj4MuhRn7S+RSacN9zEuntHamTruMtzwB\n\tTPT3Zn/JbWMkvvJbDYiOdTe3Rja28WFvFL+uEJmlcPe+GoBlkxNSkeOIy6PJUB53eOUk\n\td7FA==","X-Gm-Message-State":"AOAM531bOEvvZ9S8H2w8isSUE74W9SiX+oRB2HorKe71KtrX59HdHjOX\n\tAX+VKmdIwUpfGA+9A2jsP9kQ/GSlWaYktSwLq6LZZZHSjUc=","X-Google-Smtp-Source":"ABdhPJydjdues+PC3TXNQd0jDX+6/8nX36Q1aohjarPjfZawl4OU/ikMmqxwWg1SdBp5w8eAh8ra2ZmZpd/1lfu27I4=","X-Received":"by 2002:a25:550:: with SMTP id 77mr4287793ybf.296.1630667475874; \n\tFri, 03 Sep 2021 04:11:15 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-5-laurent.pinchart@ideasonboard.com>\n\t<797f3608-e856-3094-5e01-4c71c19cee0e@ideasonboard.com>\n\t<YTEZtNWvA059n8ty@pendragon.ideasonboard.com>\n\t<CAO5uPHPBeoK7HDoL-uDWA51Afakebw17rX3zrarpywZ9MEqqew@mail.gmail.com>\n\t<YTH//PVXb4z6rLIV@pendragon.ideasonboard.com>","In-Reply-To":"<YTH//PVXb4z6rLIV@pendragon.ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 20:11:04 +0900","Message-ID":"<CAO5uPHMeQC5HQ_CFs_Qt2MWrfKrk5X-sjVLj_yyVkAJd6Aq2Ug@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 04/12] libcamera: formats:\n\tSupport V4L2 non-contiguous formats","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19358,"web_url":"https://patchwork.libcamera.org/comment/19358/","msgid":"<YTIFn5jKXLXsNSTw@pendragon.ideasonboard.com>","date":"2021-09-03T11:23:11","subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Hiro,\n\nOn Fri, Sep 03, 2021 at 08:09:07PM +0900, Hirokazu Honda wrote:\n> On Fri, Sep 3, 2021 at 5:40 PM Laurent Pinchart wrote:\n> > On Fri, Sep 03, 2021 at 09:24:02AM +0100, Kieran Bingham wrote:\n> > > On 02/09/2021 19:07, Laurent Pinchart wrote:\n> > > > On Thu, Sep 02, 2021 at 10:43:56AM +0100, Kieran Bingham wrote:\n> > > >> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > > >>> Multi-planar frame buffers can store their planes contiguously in\n> > > >>> memory, or split them in discontiguous memory areas. Add a private\n> > > >>> function to check in which of these two categories the frame buffer\n> > > >>> belongs. This will be used to correctly handle the differences between\n> > > >>> the V4L2 single and multi planar APIs.\n> > > >>>\n> > > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > >>> ---\n> > > >>>  include/libcamera/internal/framebuffer.h |  2 ++\n> > > >>>  src/libcamera/framebuffer.cpp            | 45 +++++++++++++++++++++++-\n> > > >>>  2 files changed, 46 insertions(+), 1 deletion(-)\n> > > >>>\n> > > >>> diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h\n> > > >>> index 606aed2b4782..cd33c295466e 100644\n> > > >>> --- a/include/libcamera/internal/framebuffer.h\n> > > >>> +++ b/include/libcamera/internal/framebuffer.h\n> > > >>> @@ -21,9 +21,11 @@ public:\n> > > >>>   Private();\n> > > >>>\n> > > >>>   void setRequest(Request *request) { request_ = request; }\n> > > >>> + bool isContiguous() const { return isContiguous_; }\n> > > >>>\n> > > >>>  private:\n> > > >>>   Request *request_;\n> > > >>> + bool isContiguous_;\n> > > >>>  };\n> > > >>>\n> > > >>>  } /* namespace libcamera */\n> > > >>> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > > >>> index 53ef89bf458f..99265b44da43 100644\n> > > >>> --- a/src/libcamera/framebuffer.cpp\n> > > >>> +++ b/src/libcamera/framebuffer.cpp\n> > > >>> @@ -106,7 +106,7 @@ LOG_DEFINE_CATEGORY(Buffer)\n> > > >>>   */\n> > > >>>\n> > > >>>  FrameBuffer::Private::Private()\n> > > >>> - : request_(nullptr)\n> > > >>> + : request_(nullptr), isContiguous_(true)\n> > > >>>  {\n> > > >>>  }\n> > > >>>\n> > > >>> @@ -120,6 +120,17 @@ FrameBuffer::Private::Private()\n> > > >>>   * handlers, it is called by the pipeline handlers themselves.\n> > > >>>   */\n> > > >>>\n> > > >>> +/**\n> > > >>> + * \\fn FrameBuffer::Private::isContiguous()\n> > > >>> + * \\brief Check if the frame buffer stores planes contiguously in memory\n> > > >>> + *\n> > > >>> + * Multi-planar frame buffers can store their planes contiguously in memory, or\n> > > >>> + * split them in discontiguous memory areas. This function checks in which of\n> > > >>\n> > > >> 'split them into discontiguous'\n> > > >>\n> > > >>> + * these two categories the frame buffer belongs.\n> > > >>> + *\n> > > >>> + * \\return True if the planes are stored contiguously in memory, false otherwise\n> > > >>> + */\n> > > >>> +\n> > > >>>  /**\n> > > >>>   * \\class FrameBuffer\n> > > >>>   * \\brief Frame buffer data and its associated dynamic metadata\n> > > >>> @@ -199,9 +210,41 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> > > >>>   : Extensible(std::make_unique<Private>()), planes_(planes),\n> > > >>>     cookie_(cookie)\n> > > >>>  {\n> > > >>> + unsigned int offset = 0;\n> > > >>> +\n> > > >>>   /* \\todo Remove the assertions after sufficient testing */\n> > > >>>   for (const auto &plane : planes_)\n> > > >>>           ASSERT(plane.offset != Plane::kInvalidOffset);\n> \n> nit:\n> Shall we merge the two for loops?\n\nI've kept them separate to make it easier to remove the first one (and\nnow I wonder why I thought it would be easier...), but as the ASSERT is\nnow here to stay, I'll merge them.\n\n> At least, I would move offset to before isContiguous.\n\nDone already :-)\n\n> > > >>> +\n> > > >>> + bool isContiguous = true;\n> > > >>> + ino_t inode = 0;\n> > > >>> +\n> > > >>> + for (const auto &plane : planes_) {\n> > > >>> +         if (plane.offset != offset) {\n> > > >>> +                 isContiguous = false;\n> > > >>> +                 break;\n> > > >>> +         }\n> > > >>> +\n> > > >>> +         /*\n> > > >>> +          * Two different dmabuf file descriptors may still refer to the\n> > > >>> +          * same dmabuf instance. Check this using inodes.\n> > > >>\n> > > >> Going back to the FileDescriptor::inode() extension, if that cached the\n> > > >> inode (it can't change) on either first call, or construction, couldn't\n> > > >> this whole check be simplified to:\n> > > >>\n> > > >>            if (plane.fd.inode() != planes_[0].fd.inode()) {\n> > > >>                    isContiguous = false;\n> > > >>                    break;\n> > > >>            }\n> > > >\n> > > > This would call fstat() every time, while the fd comparison here is\n> > > > meant to skip the fstat() calls in case the numerical fd match. We could\n> > > > do\n> > >\n> > > Did you miss the part where I said \"If we cache the inode in\n> > > FileDescriptor\"?\n> >\n> > No I didn't :-)\n> >\n> > > My intention was that caching there would mean we can simplify\n> > > comparisons here, without incurring repeated fstat calls.\n> >\n> > If the numerical fds are identical, there's no need to compare the\n> > inodes, and we can avoid calling fstat() at all.\n> >\n> > > >             if (plane.fd != planes_[0].fd)\n> > > >\n> > > > if we introduced an operator==(), but as explained in a reply to the\n> > > > patch that adds inode(), I'd like to handle that later, to avoid\n> > > > blocking this series.\n> > >\n> > > Ok, I still don't see the harm in caching the inode within\n> > > FileDescriptor(), it can't change once read the first time.\n> >\n> > What we'd need to do in operator==() is to first compare the numerical\n> > fds, and only compare the inodes if the fds differ. Technically that's\n> > not an issue, but it requires deciding what the semantics of equality is\n> > as explaining in a separate e-mail, and that's the part that I'd like to\n> > defer to avoid delaying the offset fixes.\n> >\n> > > But, it can be deferred:\n> > >\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > >\n> > > >>> +          */\n> > > >>> +         if (plane.fd.fd() != planes_[0].fd.fd()) {\n> > > >>> +                 if (!inode)\n> > > >>> +                         inode = planes_[0].fd.inode();\n> \n> Shall error case (i.e plane.fd.inode() == 0) be handled?\n\nThis can only happen if the application gives us an incorrect fd, I'm\nnot sure how much we need to protect against applications doing very\nvery bad things.\n\n> > > >>> +                 if (plane.fd.inode() != inode) {\n> > > >>> +                         isContiguous = false;\n> > > >>> +                         break;\n> > > >>> +                 }\n> > > >>> +         }\n> > > >>> +\n> > > >>> +         offset += plane.length;\n> > > >>> + }\n> > > >>> +\n> > > >>> + LOG(Buffer, Debug)\n> > > >>> +         << \"Buffer is \" << (isContiguous ? \"not \" : \"\") << \"contiguous\";\n> > > >>> +\n> > > >>> + _d()->isContiguous_ = isContiguous;\n> > > >>>  }\n> > > >>>\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 9036BBD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 11:23:30 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 155FF69169;\n\tFri,  3 Sep 2021 13:23:30 +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 955BF69168\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 13:23:28 +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 07416BBE;\n\tFri,  3 Sep 2021 13:23:27 +0200 (CEST)"],"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=\"nROZV4Z3\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1630668208;\n\tbh=Vkk7/2vdbTcG4WZ8ksLu56YTKCoUxer/X394RF1VYpU=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=nROZV4Z3+e/r9s8BVNp2X/X8h7NRiNkZLWrstaqCAsD1DJVOpOLOTLxCI8tI51aGN\n\tW9sCFBY7B3lxBWwiKaAFOPY58j0nmbndWOwn75p562JjWMtu4FPhLhzZx/Y4G0XNO0\n\tXGaTVpcZMS0UCx5yOnZxhagGHwRTW42Myr/SbPbk=","Date":"Fri, 3 Sep 2021 14:23:11 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Hirokazu Honda <hiroh@chromium.org>","Message-ID":"<YTIFn5jKXLXsNSTw@pendragon.ideasonboard.com>","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>\n\t<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>\n\t<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>\n\t<ed14c57f-8b16-f2a0-a13f-fcfd4069897b@ideasonboard.com>\n\t<YTHfUK4YUZ9AMx2V@pendragon.ideasonboard.com>\n\t<CAO5uPHMD-iCK3XwM+3iiAf8PoRB65WMwKknQw2A2FzPtOOeyCg@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CAO5uPHMD-iCK3XwM+3iiAf8PoRB65WMwKknQw2A2FzPtOOeyCg@mail.gmail.com>","Subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19359,"web_url":"https://patchwork.libcamera.org/comment/19359/","msgid":"<CAO5uPHP4Gab8wydojjXvUSU7wiNWwMj1+TKPBZMXX_aorQy8fg@mail.gmail.com>","date":"2021-09-03T11:29:19","subject":"Re: [libcamera-devel] [RFC PATCH v1 07/12] libcamera: framebuffer:\n\tAllocate metadata planes at construction time","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent,\n\nOn Fri, Sep 3, 2021 at 3:11 AM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Kieran,\n>\n> On Thu, Sep 02, 2021 at 10:54:19AM +0100, Kieran Bingham wrote:\n> > On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > > The metadata planes are allocated by V4L2VideoDevice when dequeuing a\n> > > buffer. This causes the metadata planes to only be allocated after a\n> > > buffer gets dequeued, and doesn't provide any strong guarantee that\n> > > their number matches the number of FrameBuffer planes. The lack of this\n> > > invariant makes the FrameBuffer class fragile.\n> > >\n> > > As a first step towards fixing this, allocate the metadata planes when\n> > > the FrameBuffer is constructed. The FrameMetadata API should be further\n> > > improved by preventing a change in the number of planes.\n> > >\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > >  src/libcamera/framebuffer.cpp      |  2 ++\n> > >  src/libcamera/v4l2_videodevice.cpp | 12 +++++++++---\n> > >  2 files changed, 11 insertions(+), 3 deletions(-)\n> > >\n> > > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > > index 99265b44da43..c1529dfb0ad1 100644\n> > > --- a/src/libcamera/framebuffer.cpp\n> > > +++ b/src/libcamera/framebuffer.cpp\n> > > @@ -210,6 +210,8 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> > >     : Extensible(std::make_unique<Private>()), planes_(planes),\n> > >       cookie_(cookie)\n> > >  {\n> > > +   metadata_.planes.resize(planes_.size());\n\nI would set bytesused to some default value, probably 0?\nstruct Plane {\n  unsigned int bytesused = 0;\n};\n\n> > > +\n> > >     unsigned int offset = 0;\n> > >\n> > >     /* \\todo Remove the assertions after sufficient testing */\n> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > index adabd4720668..a51971879e75 100644\n> > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > @@ -1586,12 +1586,18 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n> > >     buffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL\n> > >                                 + buf.timestamp.tv_usec * 1000ULL;\n> > >\n> > > -   buffer->metadata_.planes.clear();\n> > >     if (multiPlanar) {\n> > > +           if (buf.length > buffer->metadata_.planes.size()) {\n> > > +                   LOG(V4L2, Error)\n> >\n> > Are we going to 'leak' queued FrameBuffers at this point?\n> > Is there a way to prevent this occurring at queue time rather than dequeue?\n> >\n> > In fact, Can it even happen? or should it be an ASSERT/Fatal?\n>\n> It's not supposed to happen, I'd say it's a kernel bug in that case. But\n> it's a good point, I think we can return the buffer, and mark its status\n> as erroneous. I'll do so.\n>\n> > > +                           << \"Invalid number of planes (\" << buf.length\n> > > +                           << \" != \" << buffer->metadata_.planes.size() << \")\";\n> > > +                   return nullptr;\n> > > +           }\n> > > +\n> > >             for (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> > > -                   buffer->metadata_.planes.push_back({ planes[nplane].bytesused });\n> > > +                   buffer->metadata_.planes[nplane].bytesused = planes[nplane].bytesused;\n> > >     } else {\n> > > -           buffer->metadata_.planes.push_back({ buf.bytesused });\n> > > +           buffer->metadata_.planes[0].bytesused = buf.bytesused;\n\nIs metadata_.planes[1+] kept unfilled? Shall we execute\nmetadata_.planes.resize(1)?\n\nRegards,\n-Hiro\n> > >     }\n> > >\n> > >     return buffer;\n> > >\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 663F2BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 11:29:33 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E0AA769168;\n\tFri,  3 Sep 2021 13:29:32 +0200 (CEST)","from mail-yb1-xb36.google.com (mail-yb1-xb36.google.com\n\t[IPv6:2607:f8b0:4864:20::b36])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4D2D169167\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 13:29:31 +0200 (CEST)","by mail-yb1-xb36.google.com with SMTP id k65so9483456yba.13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 04:29:31 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"nuxEQfsd\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=8/SaLEc2gXzH6KDCTWsarkYdxzPqWbqVAU0Sybpqb9Q=;\n\tb=nuxEQfsdhRTeSlBDBw5tM9cSiUOwrdMuWqSz7wTFoMlUKTdW3Ggl8QXywVkGFJYzVo\n\tSP0KxsmEBmHkEPlh2LQ9ejYjpEiLq2Zx7/xXSKlijghp6vWjybZi6PnNYywD7AZtwIuQ\n\teRcat2O8JlfbnfsK7IidOLNOpc3fgVD9RDqJc=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=8/SaLEc2gXzH6KDCTWsarkYdxzPqWbqVAU0Sybpqb9Q=;\n\tb=FZ816u/i2/SHTWgQtLzKhaLMkXfK1o6tMTvJnrQk2StJgLxkyCCFKiUe/NpUwrQqC4\n\tjz3OVn5BQlumywP8EtN70FJCd8d63/SOoe3ObiYPrWcNprPnHxjyuQwtHJHRYydrvhvR\n\tUo2tqfa3ZbvERRrYEJ/Lw8900b/ElSx4Wd8pFdmObQ1mnDT8Y+ewrLtpgf6bKSYnEx4i\n\tNZFUUftgK7cuCoNcpZXER3kdGZd3YsbI4c0uz8pMdvXCmmAGypbKv+OSmE4YvJCOSfGs\n\tyPEtUnwpXtaCSham8d9KBFfQOwluGUNdDA5V0osPv4tkF+NMMhe/sGciFCdhANuUPxrc\n\tlfqw==","X-Gm-Message-State":"AOAM5329Rxi1er1V118H+ZzgNMZZ5APMn6cLq89sGmba0QMeVXmK0cw6\n\tHVgAWNxB9FHrSdpDUwCvPM6z5yxDs2CM2GJsvn7Z2gC5GRLZNw==","X-Google-Smtp-Source":"ABdhPJxaEZiP1pkYDIkDSm+/OGCC3CUWvR1NIidEc7T/8zi6mLdtUBbY8UHiDT8S1+kujDUoEM6RmAMuDjp61+aKOR4=","X-Received":"by 2002:a25:588b:: with SMTP id\n\tm133mr3881978ybb.483.1630668570112; \n\tFri, 03 Sep 2021 04:29:30 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-8-laurent.pinchart@ideasonboard.com>\n\t<a36b82c0-e1fe-9cba-0eb7-814391fc68fe@ideasonboard.com>\n\t<YTET10o5uRrgsrEm@pendragon.ideasonboard.com>","In-Reply-To":"<YTET10o5uRrgsrEm@pendragon.ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 20:29:19 +0900","Message-ID":"<CAO5uPHP4Gab8wydojjXvUSU7wiNWwMj1+TKPBZMXX_aorQy8fg@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 07/12] libcamera: framebuffer:\n\tAllocate metadata planes at construction time","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19362,"web_url":"https://patchwork.libcamera.org/comment/19362/","msgid":"<CAO5uPHMwD76npnOAU49fJK6et0wPdAOv4ArP8L22kWTej_6nZA@mail.gmail.com>","date":"2021-09-03T11:32:52","subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent,\n\nOn Fri, Sep 3, 2021 at 8:23 PM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Hiro,\n>\n> On Fri, Sep 03, 2021 at 08:09:07PM +0900, Hirokazu Honda wrote:\n> > On Fri, Sep 3, 2021 at 5:40 PM Laurent Pinchart wrote:\n> > > On Fri, Sep 03, 2021 at 09:24:02AM +0100, Kieran Bingham wrote:\n> > > > On 02/09/2021 19:07, Laurent Pinchart wrote:\n> > > > > On Thu, Sep 02, 2021 at 10:43:56AM +0100, Kieran Bingham wrote:\n> > > > >> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> > > > >>> Multi-planar frame buffers can store their planes contiguously in\n> > > > >>> memory, or split them in discontiguous memory areas. Add a private\n> > > > >>> function to check in which of these two categories the frame buffer\n> > > > >>> belongs. This will be used to correctly handle the differences between\n> > > > >>> the V4L2 single and multi planar APIs.\n> > > > >>>\n> > > > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > >>> ---\n> > > > >>>  include/libcamera/internal/framebuffer.h |  2 ++\n> > > > >>>  src/libcamera/framebuffer.cpp            | 45 +++++++++++++++++++++++-\n> > > > >>>  2 files changed, 46 insertions(+), 1 deletion(-)\n> > > > >>>\n> > > > >>> diff --git a/include/libcamera/internal/framebuffer.h b/include/libcamera/internal/framebuffer.h\n> > > > >>> index 606aed2b4782..cd33c295466e 100644\n> > > > >>> --- a/include/libcamera/internal/framebuffer.h\n> > > > >>> +++ b/include/libcamera/internal/framebuffer.h\n> > > > >>> @@ -21,9 +21,11 @@ public:\n> > > > >>>   Private();\n> > > > >>>\n> > > > >>>   void setRequest(Request *request) { request_ = request; }\n> > > > >>> + bool isContiguous() const { return isContiguous_; }\n> > > > >>>\n> > > > >>>  private:\n> > > > >>>   Request *request_;\n> > > > >>> + bool isContiguous_;\n> > > > >>>  };\n> > > > >>>\n> > > > >>>  } /* namespace libcamera */\n> > > > >>> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > > > >>> index 53ef89bf458f..99265b44da43 100644\n> > > > >>> --- a/src/libcamera/framebuffer.cpp\n> > > > >>> +++ b/src/libcamera/framebuffer.cpp\n> > > > >>> @@ -106,7 +106,7 @@ LOG_DEFINE_CATEGORY(Buffer)\n> > > > >>>   */\n> > > > >>>\n> > > > >>>  FrameBuffer::Private::Private()\n> > > > >>> - : request_(nullptr)\n> > > > >>> + : request_(nullptr), isContiguous_(true)\n> > > > >>>  {\n> > > > >>>  }\n> > > > >>>\n> > > > >>> @@ -120,6 +120,17 @@ FrameBuffer::Private::Private()\n> > > > >>>   * handlers, it is called by the pipeline handlers themselves.\n> > > > >>>   */\n> > > > >>>\n> > > > >>> +/**\n> > > > >>> + * \\fn FrameBuffer::Private::isContiguous()\n> > > > >>> + * \\brief Check if the frame buffer stores planes contiguously in memory\n> > > > >>> + *\n> > > > >>> + * Multi-planar frame buffers can store their planes contiguously in memory, or\n> > > > >>> + * split them in discontiguous memory areas. This function checks in which of\n> > > > >>\n> > > > >> 'split them into discontiguous'\n> > > > >>\n> > > > >>> + * these two categories the frame buffer belongs.\n> > > > >>> + *\n> > > > >>> + * \\return True if the planes are stored contiguously in memory, false otherwise\n> > > > >>> + */\n> > > > >>> +\n> > > > >>>  /**\n> > > > >>>   * \\class FrameBuffer\n> > > > >>>   * \\brief Frame buffer data and its associated dynamic metadata\n> > > > >>> @@ -199,9 +210,41 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> > > > >>>   : Extensible(std::make_unique<Private>()), planes_(planes),\n> > > > >>>     cookie_(cookie)\n> > > > >>>  {\n> > > > >>> + unsigned int offset = 0;\n> > > > >>> +\n> > > > >>>   /* \\todo Remove the assertions after sufficient testing */\n> > > > >>>   for (const auto &plane : planes_)\n> > > > >>>           ASSERT(plane.offset != Plane::kInvalidOffset);\n> >\n> > nit:\n> > Shall we merge the two for loops?\n>\n> I've kept them separate to make it easier to remove the first one (and\n> now I wonder why I thought it would be easier...), but as the ASSERT is\n> now here to stay, I'll merge them.\n>\n> > At least, I would move offset to before isContiguous.\n>\n> Done already :-)\n>\n> > > > >>> +\n> > > > >>> + bool isContiguous = true;\n> > > > >>> + ino_t inode = 0;\n> > > > >>> +\n> > > > >>> + for (const auto &plane : planes_) {\n> > > > >>> +         if (plane.offset != offset) {\n> > > > >>> +                 isContiguous = false;\n> > > > >>> +                 break;\n> > > > >>> +         }\n> > > > >>> +\n> > > > >>> +         /*\n> > > > >>> +          * Two different dmabuf file descriptors may still refer to the\n> > > > >>> +          * same dmabuf instance. Check this using inodes.\n> > > > >>\n> > > > >> Going back to the FileDescriptor::inode() extension, if that cached the\n> > > > >> inode (it can't change) on either first call, or construction, couldn't\n> > > > >> this whole check be simplified to:\n> > > > >>\n> > > > >>            if (plane.fd.inode() != planes_[0].fd.inode()) {\n> > > > >>                    isContiguous = false;\n> > > > >>                    break;\n> > > > >>            }\n> > > > >\n> > > > > This would call fstat() every time, while the fd comparison here is\n> > > > > meant to skip the fstat() calls in case the numerical fd match. We could\n> > > > > do\n> > > >\n> > > > Did you miss the part where I said \"If we cache the inode in\n> > > > FileDescriptor\"?\n> > >\n> > > No I didn't :-)\n> > >\n> > > > My intention was that caching there would mean we can simplify\n> > > > comparisons here, without incurring repeated fstat calls.\n> > >\n> > > If the numerical fds are identical, there's no need to compare the\n> > > inodes, and we can avoid calling fstat() at all.\n> > >\n> > > > >             if (plane.fd != planes_[0].fd)\n> > > > >\n> > > > > if we introduced an operator==(), but as explained in a reply to the\n> > > > > patch that adds inode(), I'd like to handle that later, to avoid\n> > > > > blocking this series.\n> > > >\n> > > > Ok, I still don't see the harm in caching the inode within\n> > > > FileDescriptor(), it can't change once read the first time.\n> > >\n> > > What we'd need to do in operator==() is to first compare the numerical\n> > > fds, and only compare the inodes if the fds differ. Technically that's\n> > > not an issue, but it requires deciding what the semantics of equality is\n> > > as explaining in a separate e-mail, and that's the part that I'd like to\n> > > defer to avoid delaying the offset fixes.\n> > >\n> > > > But, it can be deferred:\n> > > >\n> > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > >\n> > > > >>> +          */\n> > > > >>> +         if (plane.fd.fd() != planes_[0].fd.fd()) {\n> > > > >>> +                 if (!inode)\n> > > > >>> +                         inode = planes_[0].fd.inode();\n> >\n> > Shall error case (i.e plane.fd.inode() == 0) be handled?\n>\n> This can only happen if the application gives us an incorrect fd, I'm\n> not sure how much we need to protect against applications doing very\n> very bad things.\n>\n\nOkay, so let's assume this is caught by other places. :-)\n\nReviewed-by: Hirokazu Honda <hiroh@chromium.org>\n\n-Hiro\n\n> > > > >>> +                 if (plane.fd.inode() != inode) {\n> > > > >>> +                         isContiguous = false;\n> > > > >>> +                         break;\n> > > > >>> +                 }\n> > > > >>> +         }\n> > > > >>> +\n> > > > >>> +         offset += plane.length;\n> > > > >>> + }\n> > > > >>> +\n> > > > >>> + LOG(Buffer, Debug)\n> > > > >>> +         << \"Buffer is \" << (isContiguous ? \"not \" : \"\") << \"contiguous\";\n> > > > >>> +\n> > > > >>> + _d()->isContiguous_ = isContiguous;\n> > > > >>>  }\n> > > > >>>\n> > > > >>>  /**\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 1F97ABDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 11:33:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6D8D76916D;\n\tFri,  3 Sep 2021 13:33:05 +0200 (CEST)","from mail-yb1-xb29.google.com (mail-yb1-xb29.google.com\n\t[IPv6:2607:f8b0:4864:20::b29])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C111569167\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 13:33:04 +0200 (CEST)","by mail-yb1-xb29.google.com with SMTP id v17so3986193ybs.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 04:33:04 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"Hz3h3abf\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=eazAJuuftovJzqOqjnRslcYn3sI5lpyoMJHkgjzR9gM=;\n\tb=Hz3h3abfolOfsHl1RungN2UWNRsir4pjLVAbUXGZjCWLs5rCCmIuVvCVWdnCIgI5M8\n\t05c3Hghm1UAKIv3mbLYikUimLlTBT0o/bmAzgdqGo75XvFUhmgBTRhlDQEUWzaWhtiaf\n\ttjcGaRXqom7sLJx2ErYgE8NF1W+FN/0yxY6Us=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=eazAJuuftovJzqOqjnRslcYn3sI5lpyoMJHkgjzR9gM=;\n\tb=NvITBfOaAPXRChArvglOUa8+DTRZryqc/SLD5Y/Zq4aSNTCgezUwuCAG9wHdjoFc/1\n\t8t3ipkzzDRfKJqjgLnokihKv1pW7Bs8wMqTVJnzD3n0Tsvr1brfkstq2Fv9FByHyAS7+\n\todLM1K5CGivZsynh70MUAuuDCghEpzCA8WLJ+WD5UoF/ixU3zglZyvr4uWaBbj6S7HG8\n\t4BeU+KwG278JhsaJfXGY2l+nh7342qP4W8C3G08zK6L53qCFs71IhVuQKNPY50bzX7Sr\n\t18/AOkdMlFItX+7vrAj9fHhoHMkPcMqr4jBcG/U4tyHsIDFmo+1v0OYbqHKk8yBBkDUj\n\tvB7g==","X-Gm-Message-State":"AOAM530QhCc7ucIowjDf6GUjLccWgPmkdeSI0icEY61qRvTmjyyin76k\n\tJFCEHWRmFVFnXJ5O3BqCpmrqb5L09cfhjvmkSvSr+IlxZSo=","X-Google-Smtp-Source":"ABdhPJxjl0lJ5AfEv1GqPMPU0a76poaJYJTlPP9DDRz6LZd+pGzksT8lS+MuF8nrgOwZ/KkVeO3vgC4Xg6K22GYgG8I=","X-Received":"by 2002:a25:6c05:: with SMTP id h5mr4062636ybc.380.1630668783623;\n\tFri, 03 Sep 2021 04:33:03 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-7-laurent.pinchart@ideasonboard.com>\n\t<b9881849-154f-18d3-6de8-38035000859c@ideasonboard.com>\n\t<YTES/l+joYeVqjDD@pendragon.ideasonboard.com>\n\t<ed14c57f-8b16-f2a0-a13f-fcfd4069897b@ideasonboard.com>\n\t<YTHfUK4YUZ9AMx2V@pendragon.ideasonboard.com>\n\t<CAO5uPHMD-iCK3XwM+3iiAf8PoRB65WMwKknQw2A2FzPtOOeyCg@mail.gmail.com>\n\t<YTIFn5jKXLXsNSTw@pendragon.ideasonboard.com>","In-Reply-To":"<YTIFn5jKXLXsNSTw@pendragon.ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 20:32:52 +0900","Message-ID":"<CAO5uPHMwD76npnOAU49fJK6et0wPdAOv4ArP8L22kWTej_6nZA@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 06/12] libcamera: framebuffer:\n\tAdd a function to check if planes are contiguous","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19364,"web_url":"https://patchwork.libcamera.org/comment/19364/","msgid":"<CAO5uPHMeo6itapwJ+wRkwbT48Gjrt5opusg9jxDYtFgRF7xTQw@mail.gmail.com>","date":"2021-09-03T11:40:16","subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent, thank you for the patch.\n\nOn Fri, Sep 3, 2021 at 5:26 PM Kieran Bingham\n<kieran.bingham@ideasonboard.com> wrote:\n>\n> On 02/09/2021 19:20, Laurent Pinchart wrote:\n> > Hi Kieran,\n> >\n> > On Thu, Sep 02, 2021 at 11:03:47AM +0100, Kieran Bingham wrote:\n> >> On 02/09/2021 05:22, Laurent Pinchart wrote:\n> >>> The number of metadata planes should always match the number of frame\n> >>> buffer planes. Enforce this by making the vector private and providing\n> >>> accessor functions.\n> >>>\n\nI am confused by the definition of Metadata::Planes.\nIt has bytesused and it is set to v4l2_buf.bytesused. It seems to\nmatch the v4l2 format plane.\nThat is, it is not always the number of frame buffer planes because\nplanes in FrameBuffer are color format planes.\n\nRegards,\n-Hiro\n\n\n> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> >>> ---\n> >>>  include/libcamera/framebuffer.h    | 10 +++++++++-\n> >>>  src/cam/camera_session.cpp         |  4 ++--\n> >>>  src/cam/file_sink.cpp              |  2 +-\n> >>>  src/libcamera/framebuffer.cpp      | 12 +++++++++---\n> >>>  src/libcamera/v4l2_videodevice.cpp | 14 +++++++-------\n> >>>  src/qcam/main_window.cpp           |  2 +-\n> >>>  src/qcam/viewfinder_gl.cpp         |  2 +-\n> >>>  src/qcam/viewfinder_qt.cpp         |  2 +-\n> >>>  src/v4l2/v4l2_camera_proxy.cpp     |  2 +-\n> >>>  9 files changed, 32 insertions(+), 18 deletions(-)\n> >>>\n> >>> diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n> >>> index fd68ed0a139d..7f2f176af691 100644\n> >>> --- a/include/libcamera/framebuffer.h\n> >>> +++ b/include/libcamera/framebuffer.h\n> >>> @@ -13,6 +13,7 @@\n> >>>  #include <vector>\n> >>>\n> >>>  #include <libcamera/base/class.h>\n> >>> +#include <libcamera/base/span.h>\n> >>>\n> >>>  #include <libcamera/file_descriptor.h>\n> >>>\n> >>> @@ -34,7 +35,14 @@ struct FrameMetadata {\n> >>>     Status status;\n> >>>     unsigned int sequence;\n> >>>     uint64_t timestamp;\n> >>> -   std::vector<Plane> planes;\n> >>> +\n> >>> +   Span<Plane> planes() { return planes_; }\n> >>> +   Span<const Plane> planes() const { return planes_; }\n> >>\n> >> Returning a span here is nice.\n> >>\n> >> This likely causes compile breakage for any external app.\n> >>\n> >> I know we're not ABI/API stable, but I wonder if we should highlight\n> >> when we do cause breakage somehow, perhaps in the commit message at\n> >> least, as we already have external users who we might want to notify.\n> >\n> > Most changes to public header files will be ABI/API breakages at this\n> > point, and users will certainly notice when they get a compilation\n> > failure :-) What value do you think this would bring, who would read\n> > those messages ? I think it could be different for things that change\n> > the ABI without breaking compilation and that would require more than a\n> > recompilation to fix, there a warning seems to have more value, but I'm\n> > also sure we'll forget from time to time, if not most of the time.\n>\n> Because I think we need to start being more aware of when we introduce\n> both ABI and API breakages.\n>\n> At least making it explicitly noted in the commit message states that we\n> were aware of the breakage at that point.\n>\n>\n> >>> +\n> >>> +private:\n> >>> +   friend class FrameBuffer;\n> >>> +\n> >>> +   std::vector<Plane> planes_;\n> >>>  };\n> >>>\n> >>>  class FrameBuffer final : public Extensible\n> >>> diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\n> >>> index 60d640f2b15c..32a373a99b72 100644\n> >>> --- a/src/cam/camera_session.cpp\n> >>> +++ b/src/cam/camera_session.cpp\n> >>> @@ -374,9 +374,9 @@ void CameraSession::processRequest(Request *request)\n> >>>                  << \" bytesused: \";\n> >>>\n> >>>             unsigned int nplane = 0;\n> >>> -           for (const FrameMetadata::Plane &plane : metadata.planes) {\n> >>> +           for (const FrameMetadata::Plane &plane : metadata.planes()) {\n> >>>                     info << plane.bytesused;\n> >>> -                   if (++nplane < metadata.planes.size())\n> >>> +                   if (++nplane < metadata.planes().size())\n> >>>                             info << \"/\";\n> >>>             }\n> >>>     }\n> >>> diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\n> >>> index 0b529e3eb767..0fc7d621f50b 100644\n> >>> --- a/src/cam/file_sink.cpp\n> >>> +++ b/src/cam/file_sink.cpp\n> >>> @@ -110,7 +110,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer)\n> >>>\n> >>>     for (unsigned int i = 0; i < buffer->planes().size(); ++i) {\n> >>>             const FrameBuffer::Plane &plane = buffer->planes()[i];\n> >>> -           const FrameMetadata::Plane &meta = buffer->metadata().planes[i];\n> >>> +           const FrameMetadata::Plane &meta = buffer->metadata().planes()[i];\n> >>>\n> >>>             uint8_t *data = planeData_[&plane];\n> >>>             unsigned int length = std::min(meta.bytesused, plane.length);\n> >>> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> >>> index c1529dfb0ad1..83cf7b83d182 100644\n> >>> --- a/src/libcamera/framebuffer.cpp\n> >>> +++ b/src/libcamera/framebuffer.cpp\n> >>> @@ -91,8 +91,14 @@ LOG_DEFINE_CATEGORY(Buffer)\n> >>>   */\n> >>>\n> >>>  /**\n> >>> - * \\var FrameMetadata::planes\n> >>> - * \\brief Array of per-plane metadata\n> >>> + * \\fn FrameMetadata::planes()\n> >>> + * \\copydoc FrameMetadata::planes() const\n> >>> + */\n> >>> +\n> >>> +/**\n> >>> + * \\fn FrameMetadata::planes() const\n> >>> + * \\brief Retrieve the array of per-plane metadata\n> >>> + * \\return The array of per-plane metadata\n> >>>   */\n> >>>\n> >>>  /**\n> >>> @@ -210,7 +216,7 @@ FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)\n> >>>     : Extensible(std::make_unique<Private>()), planes_(planes),\n> >>>       cookie_(cookie)\n> >>>  {\n> >>> -   metadata_.planes.resize(planes_.size());\n> >>> +   metadata_.planes_.resize(planes_.size());\n> >>>\n> >>>     unsigned int offset = 0;\n> >>>\n> >>> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> >>> index a51971879e75..82ddaed3656f 100644\n> >>> --- a/src/libcamera/v4l2_videodevice.cpp\n> >>> +++ b/src/libcamera/v4l2_videodevice.cpp\n> >>> @@ -1485,14 +1485,14 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n> >>>\n> >>>             if (multiPlanar) {\n> >>>                     unsigned int nplane = 0;\n> >>> -                   for (const FrameMetadata::Plane &plane : metadata.planes) {\n> >>> +                   for (const FrameMetadata::Plane &plane : metadata.planes()) {\n> >>>                             v4l2Planes[nplane].bytesused = plane.bytesused;\n> >>>                             v4l2Planes[nplane].length = buffer->planes()[nplane].length;\n> >>>                             nplane++;\n> >>>                     }\n> >>>             } else {\n> >>> -                   if (metadata.planes.size())\n> >>> -                           buf.bytesused = metadata.planes[0].bytesused;\n> >>> +                   if (metadata.planes().size())\n> >>> +                           buf.bytesused = metadata.planes()[0].bytesused;\n> >>>             }\n> >>>\n> >>>             buf.sequence = metadata.sequence;\n> >>> @@ -1587,17 +1587,17 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n> >>>                                 + buf.timestamp.tv_usec * 1000ULL;\n> >>>\n> >>>     if (multiPlanar) {\n> >>> -           if (buf.length > buffer->metadata_.planes.size()) {\n> >>> +           if (buf.length > buffer->metadata_.planes().size()) {\n> >>>                     LOG(V4L2, Error)\n> >>>                             << \"Invalid number of planes (\" << buf.length\n> >>> -                           << \" != \" << buffer->metadata_.planes.size() << \")\";\n> >>> +                           << \" != \" << buffer->metadata_.planes().size() << \")\";\n> >>>                     return nullptr;\n> >>>             }\n> >>>\n> >>>             for (unsigned int nplane = 0; nplane < buf.length; nplane++)\n> >>> -                   buffer->metadata_.planes[nplane].bytesused = planes[nplane].bytesused;\n> >>> +                   buffer->metadata_.planes()[nplane].bytesused = planes[nplane].bytesused;\n> >>>     } else {\n> >>> -           buffer->metadata_.planes[0].bytesused = buf.bytesused;\n> >>> +           buffer->metadata_.planes()[0].bytesused = buf.bytesused;\n> >>>     }\n> >>>\n> >>>     return buffer;\n> >>> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> >>> index 1536b2b5bd66..ac853e360aea 100644\n> >>> --- a/src/qcam/main_window.cpp\n> >>> +++ b/src/qcam/main_window.cpp\n> >>> @@ -756,7 +756,7 @@ void MainWindow::processViewfinder(FrameBuffer *buffer)\n> >>>\n> >>>     qDebug().noquote()\n> >>>             << QString(\"seq: %1\").arg(metadata.sequence, 6, 10, QLatin1Char('0'))\n> >>> -           << \"bytesused:\" << metadata.planes[0].bytesused\n> >>> +           << \"bytesused:\" << metadata.planes()[0].bytesused\n> >>>             << \"timestamp:\" << metadata.timestamp\n> >>>             << \"fps:\" << Qt::fixed << qSetRealNumberPrecision(2) << fps;\n> >>>\n> >>> diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp\n> >>> index 40226601f9fd..d2ef036974f4 100644\n> >>> --- a/src/qcam/viewfinder_gl.cpp\n> >>> +++ b/src/qcam/viewfinder_gl.cpp\n> >>> @@ -125,7 +125,7 @@ void ViewFinderGL::render(libcamera::FrameBuffer *buffer,\n> >>>     /*\n> >>>      * \\todo Get the stride from the buffer instead of computing it naively\n> >>>      */\n> >>> -   stride_ = buffer->metadata().planes[0].bytesused / size_.height();\n> >>> +   stride_ = buffer->metadata().planes()[0].bytesused / size_.height();\n> >>\n> >> Can this be obtained from the PixelFormatInfo now ?\n> >> or do we still not expose that to applications...\n> >\n> > PixelFormatInfo is still internal. I'm considering exposing it, but then\n> > it would be good to move the V4L2-specific part somewhere else.\n> >\n> >> Anyway, that's likely a change on top even if we could to solve the \\todo.\n> >>\n> >> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >>\n> >>>     update();\n> >>>     buffer_ = buffer;\n> >>>  }\n> >>> diff --git a/src/qcam/viewfinder_qt.cpp b/src/qcam/viewfinder_qt.cpp\n> >>> index efa1d412584b..a0bf99b0b522 100644\n> >>> --- a/src/qcam/viewfinder_qt.cpp\n> >>> +++ b/src/qcam/viewfinder_qt.cpp\n> >>> @@ -87,7 +87,7 @@ void ViewFinderQt::render(libcamera::FrameBuffer *buffer,\n> >>>     }\n> >>>\n> >>>     unsigned char *memory = mem.data();\n> >>> -   size_t size = buffer->metadata().planes[0].bytesused;\n> >>> +   size_t size = buffer->metadata().planes()[0].bytesused;\n> >>>\n> >>>     {\n> >>>             QMutexLocker locker(&mutex_);\n> >>> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> >>> index 8d8ee395954f..68e47ee81834 100644\n> >>> --- a/src/v4l2/v4l2_camera_proxy.cpp\n> >>> +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> >>> @@ -212,7 +212,7 @@ void V4L2CameraProxy::updateBuffers()\n> >>>\n> >>>             switch (fmd.status) {\n> >>>             case FrameMetadata::FrameSuccess:\n> >>> -                   buf.bytesused = fmd.planes[0].bytesused;\n> >>> +                   buf.bytesused = fmd.planes()[0].bytesused;\n> >>>                     buf.field = V4L2_FIELD_NONE;\n> >>>                     buf.timestamp.tv_sec = fmd.timestamp / 1000000000;\n> >>>                     buf.timestamp.tv_usec = fmd.timestamp % 1000000;\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 D5477BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 11:40:30 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 50B036916B;\n\tFri,  3 Sep 2021 13:40:30 +0200 (CEST)","from mail-yb1-xb2e.google.com (mail-yb1-xb2e.google.com\n\t[IPv6:2607:f8b0:4864:20::b2e])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D12B269168\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 13:40:28 +0200 (CEST)","by mail-yb1-xb2e.google.com with SMTP id j195so1566503ybg.6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 04:40:28 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"Bdfp0zP6\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=o+ys4JV5F839EovLz0lUokIRm04WMciwQM/MxOvG1mY=;\n\tb=Bdfp0zP6J5tdfmaPJHqB+sneDq2Q61x+SM2eQ1MBR+0PRnrjJ2dm/583P3IO+LV/QN\n\tZKID6mTP7CQP2k/jxWiCQEswdF/gYH+wF5cilBjCLRlC3uXJUk/UQMQaHNtnyjDLgcT+\n\t19pI0iGsuj9ezMv1sihPiyHBnCAuulf3lswDA=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=o+ys4JV5F839EovLz0lUokIRm04WMciwQM/MxOvG1mY=;\n\tb=kIYFs1WzxCDli2r+xsXBXj7zP3bXSoJoZ0X20JPikGw0uxQNBZU5vwAmAbUZ3ZWhxe\n\tTNAihoz1iQbAvI7XgR/bkI/wXjzmmU1j1j+aNKjiCjnABx1sOofw240Caq9rEafmFL3/\n\tcI/WB09tntw8yDUOUPRzMKr1WQ0YOjb1tRvm1CXnwmKyOVW9zXmctzZXJ3nZvh7jmXIY\n\t6aqm75lo/VH6y+ILPY9IdRits2SR38Qt7Vx3AmWPCgkANq97sIRCb68hom3l73OYqg8u\n\t/iM9VlBTFYjwt7wvUUqVaJtH0LcDzQEXGpf4QClPv+JWGZum1akU9s23txkSoIUj6g/4\n\tYsig==","X-Gm-Message-State":"AOAM531xg2K2LGbIfEc3Az232PHwOS4+SWLLd13hO7K8hsuJE/2UV6wz\n\tAiTBkX+iNmSFO7gJ2FIObujK9gEgajHHA8aFWhWEa7ja5gM=","X-Google-Smtp-Source":"ABdhPJwzFDEo60L8WzMzMtVf4OG/ytoQMlwZ5dQ7jOoMcA9H/Wge36zhcUE44spLsIUDXYVO14Ud3TbSmDYfuoK1aCQ=","X-Received":"by 2002:a25:550:: with SMTP id 77mr4420105ybf.296.1630669227619; \n\tFri, 03 Sep 2021 04:40:27 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-9-laurent.pinchart@ideasonboard.com>\n\t<23fb7400-0ebd-276b-52f9-863442ebf4b1@ideasonboard.com>\n\t<YTEV40OWld/Oxe4J@pendragon.ideasonboard.com>\n\t<fc5e4c45-564b-a1c7-9f87-beafd1f9749a@ideasonboard.com>","In-Reply-To":"<fc5e4c45-564b-a1c7-9f87-beafd1f9749a@ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 20:40:16 +0900","Message-ID":"<CAO5uPHMeo6itapwJ+wRkwbT48Gjrt5opusg9jxDYtFgRF7xTQw@mail.gmail.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 08/12] libcamera: framebuffer:\n\tPrevent modifying the number of metadata planes","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19366,"web_url":"https://patchwork.libcamera.org/comment/19366/","msgid":"<CAO5uPHOeja4BxDnjcFoPrEea=WEDGq0GeDiosfujc1AsshkO6Q@mail.gmail.com>","date":"2021-09-03T11:54:52","subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent, thank you for the patch.\n\nOn Fri, Sep 3, 2021 at 5:48 PM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Kieran,\n>\n> On Fri, Sep 03, 2021 at 09:29:40AM +0100, Kieran Bingham wrote:\n> > On 02/09/2021 19:26, Laurent Pinchart wrote:\n> > > On Thu, Sep 02, 2021 at 11:21:29AM +0100, Kieran Bingham wrote:\n> > >> On 02/09/2021 05:23, Laurent Pinchart wrote:\n> > >>> Cache the PixelFormatInfo instead of looking it up in every call to\n> > >>> createBuffer(). This prepare for usage of the info in queueBuffer(), to\n> > >>\n> > >> s/prepare/prepares/\n> > >>\n> > >>> avoid a looking every time a buffer is queued.\n> > >>>\n> > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > >>> ---\n> > >>>  include/libcamera/internal/v4l2_videodevice.h |  1 +\n> > >>>  src/libcamera/v4l2_videodevice.cpp            | 19 +++++++++++--------\n> > >>>  2 files changed, 12 insertions(+), 8 deletions(-)\n> > >>>\n> > >>> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h\n> > >>> index 7a145f608a5b..6096cd609b97 100644\n> > >>> --- a/include/libcamera/internal/v4l2_videodevice.h\n> > >>> +++ b/include/libcamera/internal/v4l2_videodevice.h\n> > >>> @@ -243,6 +243,7 @@ private:\n> > >>>\n> > >>>   V4L2Capability caps_;\n> > >>>   V4L2DeviceFormat format_;\n> > >>> + const PixelFormatInfo *formatInfo_;\n> > >>>\n> > >>>   enum v4l2_buf_type bufferType_;\n> > >>>   enum v4l2_memory memoryType_;\n> > >>> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > >>> index 82ddaed3656f..2d9a94c3c974 100644\n> > >>> --- a/src/libcamera/v4l2_videodevice.cpp\n> > >>> +++ b/src/libcamera/v4l2_videodevice.cpp\n> > >>> @@ -482,8 +482,8 @@ const std::string V4L2DeviceFormat::toString() const\n> > >>>   * \\param[in] deviceNode The file-system path to the video device node\n> > >>>   */\n> > >>>  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > >>> - : V4L2Device(deviceNode), cache_(nullptr), fdBufferNotifier_(nullptr),\n> > >>> -   streaming_(false)\n> > >>> + : V4L2Device(deviceNode), formatInfo_(nullptr), cache_(nullptr),\n> > >>> +   fdBufferNotifier_(nullptr), streaming_(false)\n> > >>>  {\n> > >>>   /*\n> > >>>    * We default to an MMAP based CAPTURE video device, however this will\n> > >>> @@ -586,6 +586,7 @@ int V4L2VideoDevice::open()\n> > >>>           return ret;\n> > >>>   }\n> > >>>\n> > >>> + formatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> > >>\n> > >> I shudder a little seeing code hugging the return like that ;-)\n> > >>\n> > >> But it's purely subjective, same below of course.\n> > >\n> > > We have various patterns indeed :-) I haven't been able to establish yet\n> > > what my mental process to deal with these are, sometimes it bothers me\n> > > too, but not always.\n> > >\n> > >>>   return 0;\n> > >>>  }\n> > >>>\n> > >>> @@ -681,6 +682,7 @@ int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)\n> > >>>           return ret;\n> > >>>   }\n> > >>>\n> > >>> + formatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> > >>>   return 0;\n> > >>>  }\n> > >>>\n> > >>> @@ -695,6 +697,8 @@ void V4L2VideoDevice::close()\n> > >>>   releaseBuffers();\n> > >>>   delete fdBufferNotifier_;\n> > >>>\n> > >>> + formatInfo_ = nullptr;\n> > >>> +\n> > >>>   V4L2Device::close();\n> > >>>  }\n> > >>>\n> > >>> @@ -787,6 +791,7 @@ int V4L2VideoDevice::setFormat(V4L2DeviceFormat *format)\n> > >>>           return ret;\n> > >>>\n> > >>>   format_ = *format;\n> > >>> + formatInfo_ = &PixelFormatInfo::info(format_.fourcc);\n> > >>\n> > >> Looks like we were already using that pattern though ;-)\n> > >>\n> > >>>   return 0;\n> > >>>  }\n> > >>>\n> > >>> @@ -1325,19 +1330,17 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> > >>>           planes.push_back(std::move(plane));\n> > >>>   }\n> > >>>\n> > >>> - const auto &info = PixelFormatInfo::info(format_.fourcc);\n> > >>> - if (info.isValid() && info.numPlanes() != numPlanes) {\n> > >>> + if (formatInfo_->isValid() && formatInfo_->numPlanes() != numPlanes) {\n> > >>>           ASSERT(numPlanes == 1u);\n> > >>\n> > >> Not for this patch, but I guess this assert needed a\n> > >>   \\todo Multiplanar support\n> > >>\n> > >> around it?\n> > >\n> > > No, when formatInfo_->numPlanes() != numPlanes, it means that we have a\n> > > multi-planar FrameBuffer with a single-planar V4L2 buffer format (e.g.\n> > > V4L2_PIX_FMT_NV12 as opposed to V4L2_PIX_FMT_NV12M). numPlanes must be 1\n> > > in that case, otherwise it's a kernel bug.\n> >\n> > As you're updating the conditional there anyway, could you add a short\n> > brief to explain that before the ASSERT please?\n> >\n> > I don't think that extra context is clear from just the if statement.\n> > (Perhaps there is more context outside of the hunk maybe, but it's not\n> > clear from just this diff).\n>\n> That's because this diff only deals with format caching :-) It's a good\n> point though, it's not trivial so I'll add a separate patch to document\n> this properly.\n>\n> > >> However, this patch is just for caching the formatInfo, which seems\n> > >> reasonable so\n> > >>\n> > >> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > >>\n> > >>> -         const size_t numColorPlanes = info.numPlanes();\n> > >>> -         planes.resize(numColorPlanes);\n> > >>> +         planes.resize(formatInfo_->numPlanes());\n\nBy the way, what I would not like in PixelFormatInfo(), numPlanes()\ndoes for-loops always.\nShall we add numPlanes to PixelFormatInfo?\n\nDefinitely, it is not related to this patch.\n\nReviewed-by: Hirokazu Honda <hiroh@chromium.org>\n\n-Hiro\n> > >>>           const FileDescriptor &fd = planes[0].fd;\n> > >>>           size_t offset = 0;\n> > >>> -         for (size_t i = 0; i < numColorPlanes; ++i) {\n> > >>> +         for (size_t i = 0; i < planes.size(); ++i) {\n> > >>>                   planes[i].fd = fd;\n> > >>>                   planes[i].offset = offset;\n> > >>>\n> > >>>                   /* \\todo Take the V4L2 stride into account */\n> > >>> -                 planes[i].length = info.planeSize(format_.size, i);\n> > >>> +                 planes[i].length = formatInfo_->planeSize(format_.size, i);\n> > >>>                   offset += planes[i].length;\n> > >>>           }\n> > >>>   }\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 D404FBDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 11:55:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3634D6916D;\n\tFri,  3 Sep 2021 13:55:05 +0200 (CEST)","from mail-ed1-x531.google.com (mail-ed1-x531.google.com\n\t[IPv6:2a00:1450:4864:20::531])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7591069167\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 13:55:04 +0200 (CEST)","by mail-ed1-x531.google.com with SMTP id r7so7626228edd.6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 04:55:04 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"ZzPYZBBX\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=E7uOUxChIIllnUZMu9Cx/qHeBHE76/DmAN0DoZ3VEuc=;\n\tb=ZzPYZBBXXyhM1Ja17DkY8nAuLhkowtDQZqykLGzAFEySC0tsx9pF1CPQJbzMXwVmoS\n\tVgvW+wpOElB9So+IugrfNfbqNjH63NtKpscWuQgdDCsXV7lFfqHNEcv6Zewm58fLJGmo\n\tUvq6u7ox1ywHOd/2lmfPbKoVBzc5Z1l75KEgw=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=E7uOUxChIIllnUZMu9Cx/qHeBHE76/DmAN0DoZ3VEuc=;\n\tb=j9s4BzbUmr/LbBsMuNggKxeglwCOWyC+XE9YC363sk3VhdU/gzv4VQEfcjBmXrS9mi\n\tVqr/Wj8/3S6C1xskXeIDGkm1p4fDsPFcFE9vjvCvt0D52o9NRJ+On4mXHLndYkzTwYdD\n\tS9JkrHEA5jk5gXssD9KgtP1ARIuLKD/UtrtjMcuqFxKpdJCDeL+iUc6BRvw7aE7rnwTL\n\tOsVgtsaRMCMtZ48vumTao6VOl55MyQg6QwNCT0JteAwdrfGsHICDVvm7snEiY26xUC+Q\n\t/V0ylKg1i85FV8lv8Gme8PXkOtXNg8j0S0Pw2hS1dLRvC31prMwA8213Qti9qUYH/EWd\n\t9ldw==","X-Gm-Message-State":"AOAM533olZxtHmDIUts0rb+X2K+Pc8baU05gbZcjGToP09O91qNWFHHG\n\tHsB74xACV7ZNTsmuV6x29/gahycNaDZrr1T/DF0iWIYg98M=","X-Google-Smtp-Source":"ABdhPJzXk1hLI5LCB5u7KSiDnRatCQZnMx/4Upy44ZeXSNmXiK7BFtJBv5xnXvNQrYi41gVWqD/cjGBk7yrj8LA/afI=","X-Received":"by 2002:a05:6402:caa:: with SMTP id\n\tcn10mr3556559edb.202.1630670103951; \n\tFri, 03 Sep 2021 04:55:03 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-10-laurent.pinchart@ideasonboard.com>\n\t<ef9325cc-41f1-efa3-109d-0e5dd238317a@ideasonboard.com>\n\t<YTEXaTYw2x9VxCQQ@pendragon.ideasonboard.com>\n\t<9791e06e-b8fc-bae9-fb9c-b39f195277a4@ideasonboard.com>\n\t<YTHhWnGDl1+8vBrr@pendragon.ideasonboard.com>","In-Reply-To":"<YTHhWnGDl1+8vBrr@pendragon.ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 20:54:52 +0900","Message-ID":"<CAO5uPHOeja4BxDnjcFoPrEea=WEDGq0GeDiosfujc1AsshkO6Q@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 09/12] libcamera:\n\tv4l2_videodevice: Cache PixelFormatInfo","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19368,"web_url":"https://patchwork.libcamera.org/comment/19368/","msgid":"<CAO5uPHPmrS6gCibD=uAikF8u4xdr=SSTvVJ4OtSanjiosze9-w@mail.gmail.com>","date":"2021-09-03T12:25:17","subject":"Re: [libcamera-devel] [RFC PATCH v1 10/12] libcamera:\n\tv4l2_videodevice: Coalesce planes when queuing buffer","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent, thank you for the patch.\n\nOn Thu, Sep 2, 2021 at 8:25 PM Kieran Bingham\n<kieran.bingham@ideasonboard.com> wrote:\n>\n> On 02/09/2021 05:23, Laurent Pinchart wrote:\n> > When queueing a buffer to a V4L2VideoDevice, the number of planes in the\n> > FrameBuffer may not match the number of V4L2 buffer planes if the\n> > PixelFormat is multi-planar (has multiple colour planes) and the V4L2\n> > format is single-planar (has a single buffer plane). In this case, we\n> > need to coalesce all FrameBuffer planes into a single V4L2 buffer plane.\n> > Do so, and add validity checks to reject frame buffers that can't be\n> > described using a single V4L2 buffer plane.\n>\n>\n> Yikes ;-) lets see...\n>\n> >\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  src/libcamera/v4l2_videodevice.cpp | 68 +++++++++++++++++++++++++-----\n> >  1 file changed, 58 insertions(+), 10 deletions(-)\n> >\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 2d9a94c3c974..79cb792117d5 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -22,10 +22,12 @@\n> >\n> >  #include <libcamera/base/event_notifier.h>\n> >  #include <libcamera/base/log.h>\n> > +#include <libcamera/base/utils.h>\n> >\n> >  #include <libcamera/file_descriptor.h>\n> >\n> >  #include \"libcamera/internal/formats.h\"\n> > +#include \"libcamera/internal/framebuffer.h\"\n> >  #include \"libcamera/internal/media_device.h\"\n> >  #include \"libcamera/internal/media_object.h\"\n> >\n> > @@ -1468,10 +1470,21 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n> >\n> >       bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type);\n> >       const std::vector<FrameBuffer::Plane> &planes = buffer->planes();\n> > +     unsigned int numV4l2Planes = format_.fourcc == formatInfo_->v4l2Format[0]\n> > +                                ? 1 : planes.size();\n\nnit const unsigned int.\nI would replace formatInfo->numPlanes() with planes.size().\n\n>\n> Aha, ok so that's how we recover the planar/multiplanar information I\n> was concerned we had lost earlier.\n>\n> I wonder if we should set/cache numV4l2Planes_ when we set formatInfo_\n> as they are so closely paired .. But it's a trivial operation here.\n>\n>\n>\n> > +\n> > +     /*\n> > +      * If the frame buffer has multiple planes and the V4L2 format requires\n> > +      * contiguous planes, ensure that's the case.\n> > +      */\n> > +     if (planes.size() != numV4l2Planes && !buffer->_d()->isContiguous()) {\n> > +             LOG(V4L2, Error) << \"Device format requires contiguous buffer\";\n> > +             return -EINVAL;\n> > +     }\n> >\n> >       if (buf.memory == V4L2_MEMORY_DMABUF) {\n> >               if (multiPlanar) {\n> > -                     for (unsigned int p = 0; p < planes.size(); ++p)\n> > +                     for (unsigned int p = 0; p < numV4l2Planes; ++p)\n> >                               v4l2Planes[p].m.fd = planes[p].fd.fd();\n> >               } else {\n> >                       buf.m.fd = planes[0].fd.fd();\n> > @@ -1479,23 +1492,58 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n> >       }\n> >\n> >       if (multiPlanar) {\n> > -             buf.length = planes.size();\n> > +             buf.length = numV4l2Planes;\n> >               buf.m.planes = v4l2Planes;\n> >       }\n> >\n> >       if (V4L2_TYPE_IS_OUTPUT(buf.type)) {\n> >               const FrameMetadata &metadata = buffer->metadata();\n> >\n> > -             if (multiPlanar) {\n> > -                     unsigned int nplane = 0;\n> > -                     for (const FrameMetadata::Plane &plane : metadata.planes()) {\n> > -                             v4l2Planes[nplane].bytesused = plane.bytesused;\n> > -                             v4l2Planes[nplane].length = buffer->planes()[nplane].length;\n> > -                             nplane++;\n> > +             if (numV4l2Planes != planes.size()) {\n> > +                     /*\n> > +                      * If we have a multi-planar buffer with a V4L2\n> > +                      * single-planar format, coalesce all planes. The length\n> > +                      * and number of bytes used may only differ in the last\n> > +                      * plane as any other situation can't be represented.\n> > +                      */\n> > +                     unsigned int bytesused = 0;\n> > +                     unsigned int length = 0;\n> > +\n> > +                     for (auto [i, plane] : utils::enumerate(planes)) {\n> > +                             bytesused += metadata.planes()[i].bytesused;\n> > +                             length += plane.length;\n> > +\n> > +                             if (i != planes.size() - 1 && bytesused != length) {\n>\n> Ah, i almost thought that was wrong, but it's\n>    \"If we're not the last plane, and the plane is not fully used...\"\n>\n> So it's fine.\n>\n>\n> > +                                     LOG(V4L2, Error)\n> > +                                             << \"Holes in multi-planar buffer not supported\";\n> > +                                     return -EINVAL;\n> > +                             }\n> > +                     }\n> > +\n> > +                     if (multiPlanar) {\n> > +                             v4l2Planes[0].bytesused = bytesused;\n> > +                             v4l2Planes[0].length = length;\n\nI think multiPlanar must be false here.\n\nv4l2 format | plane.size() | numV4L2Planes |     numV4L2Planes ==\nplanes.size()\nmulti          |         1        |      planes.size() |          true\nmulti          |         2+      |      planes.size() |           true\nsingle        |         1        |       1                 |           true\nsingle        |         2+        |       1                 |           false\n\nSo I would implement as follows.\nif (multiPlanar) {\n   ...\n} else {\n  if (numV4l2Planes != planes.size()) {\n    ...\n  } else {\n     ...\n  }\n}\n\n\n> > +                     } else {\n> > +                             buf.bytesused = bytesused;\n> > +                             buf.length = length;\n> > +                     }\n> > +             } else if (multiPlanar) {\n> > +                     /*\n> > +                      * If we use the multi-planar API, fill in the planes.\n> > +                      * The number of planes in the frame buffer and in the\n> > +                      * V4L2 buffer is guaranteed to be equal at this point.\n> > +                      */\n> > +                     for (auto [i, plane] : utils::enumerate(planes)) {\n> > +                             v4l2Planes[i].bytesused = metadata.planes()[i].bytesused;\n> > +                             v4l2Planes[i].length = plane.length;\n> >                       }\n> >               } else {\n> > -                     if (metadata.planes().size())\n>\n> Guaranteeing the planes are there is much nicer here.\n>\n> Phew, this patch 'looks' a lot more complex than it actually is,\n>\n> So I think ... this is fine.\n>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>\n> > -                             buf.bytesused = metadata.planes()[0].bytesused;\n> > +                     /*\n> > +                      * Single-planar API with a single plane in the buffer\n> > +                      * is trivial to handle.\n> > +                      */\n> > +                     buf.bytesused = metadata.planes()[0].bytesused;\n> > +                     buf.length = planes[0].length;\n> >               }\n> >\n> >               buf.sequence = metadata.sequence;\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 127A4BDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Sep 2021 12:25:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 815A16916A;\n\tFri,  3 Sep 2021 14:25:36 +0200 (CEST)","from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com\n\t[IPv6:2a00:1450:4864:20::52f])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id CB5E469167\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Sep 2021 14:25:35 +0200 (CEST)","by mail-ed1-x52f.google.com with SMTP id dm15so7738141edb.10\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 03 Sep 2021 05:25:35 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"EWQHpALJ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=GJWA9XMDRKSCAnyvJDkgM8FPn8eIK22CX5QT21G3G/0=;\n\tb=EWQHpALJq7EUQ23pD6/RxdP46edawtfzJNiqJpoBpyECrP/VDA6zTVO+pbyQKB+Ji0\n\to4WqvTEOydgI9EcTT+CVuR8/SvNir92uVKhjlMVALZrbSpTXnvDULPdZWp8ZaX9SRx1a\n\t5wPeqp+LesxveIG8eh1+gdnR6s2vCPLMrlyJY=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=GJWA9XMDRKSCAnyvJDkgM8FPn8eIK22CX5QT21G3G/0=;\n\tb=OhihOghVKOpRe/K+d0EPq2gKXJkxvX3CX+1A/pH9gNsezGrMm/V4+ZpkbLH6/poK8A\n\t/JXgfKTo7w+lfUxgNUIsXl2n93RcS3eLc4FIwIWQrqOc1emZcbz9f278if4jpcd+ycDx\n\tRzLCxz/0dJ7e4ft/i6KoiwMbif7drV33nNZzqqUMouCaI/sS3U2nCXxS9Air7hadUWst\n\t+8muAa/UqyQYeSRXxtx3rL0tWRPjBHOtadu5MN/1VxWvINQ6uIRlFmSaQi3jhUVZviw8\n\tyMdvypdlfFHsIeWwoSX8p/iWZAnAmhwQzXDMZWH9hpTfTmyuaIgLnJysCoKc+6DPfWYH\n\tPIew==","X-Gm-Message-State":"AOAM532+9FZgIwm+xC8+kqTKh/np/IarZGs06Q+Yzb9KJipqdvAJxNSI\n\tevzsR+1GGYHa9EKs1fqvPaAwOCInLIz7nF0SoRj75w==","X-Google-Smtp-Source":"ABdhPJyAMIEvJ2ULGoH9AXzNr47VDfMNjEvceZi9lN5se3+FzWcxMC7/HoTF4U82LyqUo54rl8oCPhs8jhIFthLAmEw=","X-Received":"by 2002:a05:6402:caa:: with SMTP id\n\tcn10mr3691189edb.202.1630671935363; \n\tFri, 03 Sep 2021 05:25:35 -0700 (PDT)","MIME-Version":"1.0","References":"<20210902042303.2254-1-laurent.pinchart@ideasonboard.com>\n\t<20210902042303.2254-11-laurent.pinchart@ideasonboard.com>\n\t<79a6c006-d7ed-2efa-1ff8-41d6b101fbb9@ideasonboard.com>","In-Reply-To":"<79a6c006-d7ed-2efa-1ff8-41d6b101fbb9@ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Fri, 3 Sep 2021 21:25:17 +0900","Message-ID":"<CAO5uPHPmrS6gCibD=uAikF8u4xdr=SSTvVJ4OtSanjiosze9-w@mail.gmail.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH v1 10/12] libcamera:\n\tv4l2_videodevice: Coalesce planes when queuing buffer","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":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]