[{"id":19009,"web_url":"https://patchwork.libcamera.org/comment/19009/","msgid":"<YSQR1/vsLxiK7UAT@pendragon.ideasonboard.com>","date":"2021-08-23T21:23:35","subject":"Re: [libcamera-devel] [RFC PATCH 1/3] android:\n\tgeneric_camera_buffer: Correct buffer mapping","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Hiro,\n\nThank you for the patch.\n\nOn Mon, Aug 23, 2021 at 09:43:19PM +0900, Hirokazu Honda wrote:\n> buffer_hanlde_t doesn't provide sufficient info to map a buffer\n\ns/buffer_hanlde_t/buffer_handle_t/\n\n> properly. cros::CameraBufferManager enables handling the buffer on\n> ChromeOS, but no way is provided for other platforms.\n> \n> Therefore, we put the assumption that planes are in the same buffer\n> and they are consecutive. This modifies the way of mapping in\n> generic_camera_buffer with the assumption.\n> \n> Signed-off-by: Hirokazu Honda <hiroh@chromium.org>\n> ---\n>  src/android/camera_buffer.h              | 14 +++-\n>  src/android/camera_stream.cpp            |  4 +-\n>  src/android/mm/cros_camera_buffer.cpp    |  7 +-\n>  src/android/mm/generic_camera_buffer.cpp | 82 +++++++++++++++++-------\n>  4 files changed, 78 insertions(+), 29 deletions(-)\n> \n> diff --git a/src/android/camera_buffer.h b/src/android/camera_buffer.h\n> index c4e3a9e7..87df2570 100644\n> --- a/src/android/camera_buffer.h\n> +++ b/src/android/camera_buffer.h\n> @@ -11,13 +11,17 @@\n>  \n>  #include <libcamera/base/class.h>\n>  #include <libcamera/base/span.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/pixel_format.h>\n>  \n>  class CameraBuffer final : public libcamera::Extensible\n>  {\n>  \tLIBCAMERA_DECLARE_PRIVATE()\n>  \n>  public:\n> -\tCameraBuffer(buffer_handle_t camera3Buffer, int flags);\n> +\tCameraBuffer(buffer_handle_t camera3Buffer,\n> +\t\t     libcamera::PixelFormat pixelFormat,\n> +\t\t     const libcamera::Size &size, int flags);\n\nI'm not thrilled by the idea of having to pass a format and size, but as\nyou correctly explained, buffer_handle_t isn't enough, so I don't see\nany other option :-(\n\n>  \t~CameraBuffer();\n>  \n>  \tbool isValid() const;\n> @@ -31,8 +35,12 @@ public:\n>  };\n>  \n>  #define PUBLIC_CAMERA_BUFFER_IMPLEMENTATION\t\t\t\t\\\n> -CameraBuffer::CameraBuffer(buffer_handle_t camera3Buffer, int flags)\t\\\n> -\t: Extensible(std::make_unique<Private>(this, camera3Buffer, flags)) \\\n> +CameraBuffer::CameraBuffer(buffer_handle_t camera3Buffer,\t\t\\\n> +\t\t\t   libcamera::PixelFormat pixelFormat,\t\t\\\n> +\t\t\t   const libcamera::Size &size, int flags)\t\\\n> +\t: Extensible(std::make_unique<Private>(this, camera3Buffer,\t\\\n> +\t\t\t\t\t       pixelFormat, size,\t\\\n> +\t\t\t\t\t       flags))\t\t\t\\\n>  {\t\t\t\t\t\t\t\t\t\\\n>  }\t\t\t\t\t\t\t\t\t\\\n>  CameraBuffer::~CameraBuffer()\t\t\t\t\t\t\\\n> diff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp\n> index 61b44183..01909ec7 100644\n> --- a/src/android/camera_stream.cpp\n> +++ b/src/android/camera_stream.cpp\n> @@ -110,7 +110,9 @@ int CameraStream::process(const libcamera::FrameBuffer &source,\n>  \t * \\todo Buffer mapping and processing should be moved to a\n>  \t * separate thread.\n>  \t */\n> -\tCameraBuffer dest(camera3Dest, PROT_READ | PROT_WRITE);\n> +\tconst StreamConfiguration &output = configuration();\n> +\tCameraBuffer dest(camera3Dest, formats::MJPEG, output.size,\n> +\t\t\t  PROT_READ | PROT_WRITE);\n>  \tif (!dest.isValid()) {\n>  \t\tLOG(HAL, Error) << \"Failed to map android blob buffer\";\n>  \t\treturn -EINVAL;\n> diff --git a/src/android/mm/cros_camera_buffer.cpp b/src/android/mm/cros_camera_buffer.cpp\n> index e8783ff8..50732637 100644\n> --- a/src/android/mm/cros_camera_buffer.cpp\n> +++ b/src/android/mm/cros_camera_buffer.cpp\n> @@ -20,8 +20,9 @@ class CameraBuffer::Private : public Extensible::Private\n>  \tLIBCAMERA_DECLARE_PUBLIC(CameraBuffer)\n>  \n>  public:\n> -\tPrivate(CameraBuffer *cameraBuffer,\n> -\t\tbuffer_handle_t camera3Buffer, int flags);\n> +\tPrivate(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer,\n> +\t\tlibcamera::PixelFormat pixelFormat, const libcamera::Size &size,\n> +\t\tint flags);\n>  \t~Private();\n>  \n>  \tbool isValid() const { return valid_; }\n> @@ -46,6 +47,8 @@ private:\n>  \n>  CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer,\n>  \t\t\t       buffer_handle_t camera3Buffer,\n> +\t\t\t       [[maybe_unused]] libcamera::PixelFormat pixelFormat,\n> +\t\t\t       [[maybe_unused]] const libcamera::Size &size,\n>  \t\t\t       [[maybe_unused]] int flags)\n>  \t: handle_(camera3Buffer), numPlanes_(0), valid_(false),\n>  \t  registered_(false)\n> diff --git a/src/android/mm/generic_camera_buffer.cpp b/src/android/mm/generic_camera_buffer.cpp\n> index b3af194c..3127069f 100644\n> --- a/src/android/mm/generic_camera_buffer.cpp\n> +++ b/src/android/mm/generic_camera_buffer.cpp\n> @@ -12,6 +12,7 @@\n>  \n>  #include <libcamera/base/log.h>\n>  \n> +#include \"libcamera/internal/formats.h\"\n>  #include \"libcamera/internal/mapped_framebuffer.h\"\n>  \n>  using namespace libcamera;\n> @@ -24,8 +25,9 @@ class CameraBuffer::Private : public Extensible::Private,\n>  \tLIBCAMERA_DECLARE_PUBLIC(CameraBuffer)\n>  \n>  public:\n> -\tPrivate(CameraBuffer *cameraBuffer,\n> -\t\tbuffer_handle_t camera3Buffer, int flags);\n> +\tPrivate(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer,\n> +\t\tlibcamera::PixelFormat pixelFormat, const libcamera::Size &size,\n> +\t\tint flags);\n>  \t~Private();\n>  \n>  \tunsigned int numPlanes() const;\n> @@ -33,35 +35,69 @@ public:\n>  \tSpan<uint8_t> plane(unsigned int plane);\n>  \n>  \tsize_t jpegBufferSize(size_t maxJpegBufferSize) const;\n> +\n> +private:\n> +\t/* \\todo remove planes_ is added to MappedBuffer. */\n\nI'm not sure to follow you. Do you mean \"Remove planes_ when it will be\nadded to MappedBuffer\" maybe ?\n\n> +\tstd::vector<Span<uint8_t>> planes_;\n>  };\n>  \n>  CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer,\n> -\t\t\t       buffer_handle_t camera3Buffer, int flags)\n> +\t\t\t       buffer_handle_t camera3Buffer,\n> +\t\t\t       libcamera::PixelFormat pixelFormat,\n> +\t\t\t       const libcamera::Size &size, int flags)\n>  {\n> -\tmaps_.reserve(camera3Buffer->numFds);\n>  \terror_ = 0;\n>  \n\nI'd add a comment here:\n\n\t/*\n\t * As Android doesn't offer an API to query buffer layouts, assume for\n\t * now that the buffer is backed by a single dmabuf, with planes being\n\t * stored contiguously.\n\t */\n\n> +\tint fd = -1;\n>  \tfor (int i = 0; i < camera3Buffer->numFds; i++) {\n> -\t\tif (camera3Buffer->data[i] == -1)\n> -\t\t\tcontinue;\n> -\n> -\t\toff_t length = lseek(camera3Buffer->data[i], 0, SEEK_END);\n> -\t\tif (length < 0) {\n> -\t\t\terror_ = -errno;\n> -\t\t\tLOG(HAL, Error) << \"Failed to query plane length\";\n> +\t\tif (camera3Buffer->data[i] != -1) {\n> +\t\t\tfd = camera3Buffer->data[i];\n>  \t\t\tbreak;\n>  \t\t}\n> +\t}\n\nCould we verify the assumption ? Maybe something like\n\n\tint fd = -1;\n\tfor (int i = 0; i < camera3Buffer->numFds; i++) {\n\t\tif (camera3Buffer->data[i] == -1 ||\n\t\t    camera3Buffer->data[i] == fd)\n\t\t\tcontinue;\n\n\t\tif (fd != -1)\n\t\t\tLOG(HAL, Fatal)\n\t\t\t\t<< \"Discontiguous planes are not supported\";\n\t\tfd = camera3Buffer->data[i];\n\t}\n\n>  \n> -\t\tvoid *address = mmap(nullptr, length, flags, MAP_SHARED,\n> -\t\t\t\t     camera3Buffer->data[i], 0);\n> -\t\tif (address == MAP_FAILED) {\n> -\t\t\terror_ = -errno;\n> -\t\t\tLOG(HAL, Error) << \"Failed to mmap plane\";\n> -\t\t\tbreak;\n> -\t\t}\n> +\tif (fd != -1) {\n\nThat should be\n\n\tif (fd == -1) {\n\n> +\t\terror_ = EINVAL;\n\n-EINVAL\n\n> +\t\tLOG(HAL, Error) << \"No valid file descriptor\";\n> +\t\treturn;\n> +\t}\n> +\n> +\tconst auto &info = libcamera::PixelFormatInfo::info(pixelFormat);\n> +\tif (!info.isValid()) {\n> +\t\terror_ = EINVAL;\n\n-EINVAL\n\n> +\t\tLOG(HAL, Error) << \"Invalid pixel format: \"\n> +\t\t\t\t<< pixelFormat.toString();\n> +\t\treturn;\n> +\t}\n> +\n> +\tsize_t bufferLength = lseek(fd, 0, SEEK_END);\n> +\tif (bufferLength < 0) {\n\nThis comparison is always false, as size_t is unsigned. The correct data\ntype if off_t for the return value of lseek.\n\n> +\t\terror_ = -errno;\n> +\t\tLOG(HAL, Error) << \"Failed to get buffer length\";\n> +\t\treturn;\n> +\t}\n> +\n> +\tvoid *address = mmap(nullptr, bufferLength, flags, MAP_SHARED, fd, 0);\n> +\tif (address == MAP_FAILED) {\n> +\t\terror_ = -errno;\n> +\t\tLOG(HAL, Error) << \"Failed to mmap plane\";\n> +\t\treturn;\n> +\t}\n> +\tmaps_.emplace_back(static_cast<uint8_t *>(address), bufferLength);\n> +\n> +\tconst unsigned int numPlanes = info.numPlanes();\n> +\tplanes_.resize(numPlanes);\n> +\tunsigned int offset = 0;\n> +\tfor (unsigned int i = 0; i < numPlanes; ++i) {\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\nIt may make sense to move this calculation to a new helper function in\nlibcamera::PixelFormatInfo at some point, but it's fine for now.\n\n> +\n> +\t\tplanes_[i] = libcamera::Span<uint8_t>(\n> +\t\t\tstatic_cast<uint8_t *>(address) + offset, planeSize);\n>  \n> -\t\tmaps_.emplace_back(static_cast<uint8_t *>(address),\n> -\t\t\t\t   static_cast<size_t>(length));\n> +\t\toffset += planeSize;\n>  \t}\n>  }\n>  \n> @@ -71,15 +107,15 @@ CameraBuffer::Private::~Private()\n>  \n>  unsigned int CameraBuffer::Private::numPlanes() const\n>  {\n> -\treturn maps_.size();\n> +\treturn planes_.size();\n>  }\n>  \n>  Span<uint8_t> CameraBuffer::Private::plane(unsigned int plane)\n>  {\n> -\tif (plane >= maps_.size())\n> +\tif (plane >= planes_.size())\n>  \t\treturn {};\n>  \n> -\treturn maps_[plane];\n> +\treturn planes_[plane];\n>  }\n>  \n>  size_t CameraBuffer::Private::jpegBufferSize(size_t maxJpegBufferSize) const","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 740D6BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 23 Aug 2021 21:23:48 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DEF80688A6;\n\tMon, 23 Aug 2021 23:23:47 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 674EC6025B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 23 Aug 2021 23:23:46 +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 C88472A5;\n\tMon, 23 Aug 2021 23:23:45 +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=\"Cmq7/e0p\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629753826;\n\tbh=T1Xk0RRTi+7fKDDg2X4mMnsH7PSZ1at4yK9+6XxXp2I=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Cmq7/e0pwXLmQ3B1I4HcoBfijQx1kK1T1jrTODTKu+AOMAamL/Ne8WEDjVgqEbVBY\n\tNbQBgBCAJmo69wvh8rfN8Rmtw7bHih2LPsd/mkC97B16OGfBRoRK8xgvyAdoz/cgKp\n\tAUlnwlHVpEVZcx1UlrK2RJS78WqsxA7StPBTPV8I=","Date":"Tue, 24 Aug 2021 00:23:35 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Hirokazu Honda <hiroh@chromium.org>","Message-ID":"<YSQR1/vsLxiK7UAT@pendragon.ideasonboard.com>","References":"<20210823124321.980847-1-hiroh@chromium.org>\n\t<20210823124321.980847-2-hiroh@chromium.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20210823124321.980847-2-hiroh@chromium.org>","Subject":"Re: [libcamera-devel] [RFC PATCH 1/3] android:\n\tgeneric_camera_buffer: Correct buffer mapping","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":19129,"web_url":"https://patchwork.libcamera.org/comment/19129/","msgid":"<CAAFQd5D3O2L3eBiq7MqBHpLvV5-kgUoqMvtbZd87AQ16y9b3dQ@mail.gmail.com>","date":"2021-08-27T09:33:25","subject":"Re: [libcamera-devel] [RFC PATCH 1/3] android:\n\tgeneric_camera_buffer: Correct buffer mapping","submitter":{"id":9,"url":"https://patchwork.libcamera.org/api/people/9/","name":"Tomasz Figa","email":"tfiga@chromium.org"},"content":"On Tue, Aug 24, 2021 at 6:23 AM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Hiro,\n>\n> Thank you for the patch.\n>\n> On Mon, Aug 23, 2021 at 09:43:19PM +0900, Hirokazu Honda wrote:\n> > buffer_hanlde_t doesn't provide sufficient info to map a buffer\n>\n> s/buffer_hanlde_t/buffer_handle_t/\n>\n> > properly. cros::CameraBufferManager enables handling the buffer on\n> > ChromeOS, but no way is provided for other platforms.\n> >\n> > Therefore, we put the assumption that planes are in the same buffer\n> > and they are consecutive. This modifies the way of mapping in\n> > generic_camera_buffer with the assumption.\n> >\n> > Signed-off-by: Hirokazu Honda <hiroh@chromium.org>\n> > ---\n> >  src/android/camera_buffer.h              | 14 +++-\n> >  src/android/camera_stream.cpp            |  4 +-\n> >  src/android/mm/cros_camera_buffer.cpp    |  7 +-\n> >  src/android/mm/generic_camera_buffer.cpp | 82 +++++++++++++++++-------\n> >  4 files changed, 78 insertions(+), 29 deletions(-)\n> >\n> > diff --git a/src/android/camera_buffer.h b/src/android/camera_buffer.h\n> > index c4e3a9e7..87df2570 100644\n> > --- a/src/android/camera_buffer.h\n> > +++ b/src/android/camera_buffer.h\n> > @@ -11,13 +11,17 @@\n> >\n> >  #include <libcamera/base/class.h>\n> >  #include <libcamera/base/span.h>\n> > +#include <libcamera/geometry.h>\n> > +#include <libcamera/pixel_format.h>\n> >\n> >  class CameraBuffer final : public libcamera::Extensible\n> >  {\n> >       LIBCAMERA_DECLARE_PRIVATE()\n> >\n> >  public:\n> > -     CameraBuffer(buffer_handle_t camera3Buffer, int flags);\n> > +     CameraBuffer(buffer_handle_t camera3Buffer,\n> > +                  libcamera::PixelFormat pixelFormat,\n> > +                  const libcamera::Size &size, int flags);\n>\n> I'm not thrilled by the idea of having to pass a format and size, but as\n> you correctly explained, buffer_handle_t isn't enough, so I don't see\n> any other option :-(\n>\n> >       ~CameraBuffer();\n> >\n> >       bool isValid() const;\n> > @@ -31,8 +35,12 @@ public:\n> >  };\n> >\n> >  #define PUBLIC_CAMERA_BUFFER_IMPLEMENTATION                          \\\n> > -CameraBuffer::CameraBuffer(buffer_handle_t camera3Buffer, int flags) \\\n> > -     : Extensible(std::make_unique<Private>(this, camera3Buffer, flags)) \\\n> > +CameraBuffer::CameraBuffer(buffer_handle_t camera3Buffer,            \\\n> > +                        libcamera::PixelFormat pixelFormat,          \\\n> > +                        const libcamera::Size &size, int flags)      \\\n> > +     : Extensible(std::make_unique<Private>(this, camera3Buffer,     \\\n> > +                                            pixelFormat, size,       \\\n> > +                                            flags))                  \\\n> >  {                                                                    \\\n> >  }                                                                    \\\n> >  CameraBuffer::~CameraBuffer()                                                \\\n> > diff --git a/src/android/camera_stream.cpp b/src/android/camera_stream.cpp\n> > index 61b44183..01909ec7 100644\n> > --- a/src/android/camera_stream.cpp\n> > +++ b/src/android/camera_stream.cpp\n> > @@ -110,7 +110,9 @@ int CameraStream::process(const libcamera::FrameBuffer &source,\n> >        * \\todo Buffer mapping and processing should be moved to a\n> >        * separate thread.\n> >        */\n> > -     CameraBuffer dest(camera3Dest, PROT_READ | PROT_WRITE);\n> > +     const StreamConfiguration &output = configuration();\n> > +     CameraBuffer dest(camera3Dest, formats::MJPEG, output.size,\n> > +                       PROT_READ | PROT_WRITE);\n> >       if (!dest.isValid()) {\n> >               LOG(HAL, Error) << \"Failed to map android blob buffer\";\n> >               return -EINVAL;\n> > diff --git a/src/android/mm/cros_camera_buffer.cpp b/src/android/mm/cros_camera_buffer.cpp\n> > index e8783ff8..50732637 100644\n> > --- a/src/android/mm/cros_camera_buffer.cpp\n> > +++ b/src/android/mm/cros_camera_buffer.cpp\n> > @@ -20,8 +20,9 @@ class CameraBuffer::Private : public Extensible::Private\n> >       LIBCAMERA_DECLARE_PUBLIC(CameraBuffer)\n> >\n> >  public:\n> > -     Private(CameraBuffer *cameraBuffer,\n> > -             buffer_handle_t camera3Buffer, int flags);\n> > +     Private(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer,\n> > +             libcamera::PixelFormat pixelFormat, const libcamera::Size &size,\n> > +             int flags);\n> >       ~Private();\n> >\n> >       bool isValid() const { return valid_; }\n> > @@ -46,6 +47,8 @@ private:\n> >\n> >  CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer,\n> >                              buffer_handle_t camera3Buffer,\n> > +                            [[maybe_unused]] libcamera::PixelFormat pixelFormat,\n> > +                            [[maybe_unused]] const libcamera::Size &size,\n> >                              [[maybe_unused]] int flags)\n> >       : handle_(camera3Buffer), numPlanes_(0), valid_(false),\n> >         registered_(false)\n> > diff --git a/src/android/mm/generic_camera_buffer.cpp b/src/android/mm/generic_camera_buffer.cpp\n> > index b3af194c..3127069f 100644\n> > --- a/src/android/mm/generic_camera_buffer.cpp\n> > +++ b/src/android/mm/generic_camera_buffer.cpp\n> > @@ -12,6 +12,7 @@\n> >\n> >  #include <libcamera/base/log.h>\n> >\n> > +#include \"libcamera/internal/formats.h\"\n> >  #include \"libcamera/internal/mapped_framebuffer.h\"\n> >\n> >  using namespace libcamera;\n> > @@ -24,8 +25,9 @@ class CameraBuffer::Private : public Extensible::Private,\n> >       LIBCAMERA_DECLARE_PUBLIC(CameraBuffer)\n> >\n> >  public:\n> > -     Private(CameraBuffer *cameraBuffer,\n> > -             buffer_handle_t camera3Buffer, int flags);\n> > +     Private(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer,\n> > +             libcamera::PixelFormat pixelFormat, const libcamera::Size &size,\n> > +             int flags);\n> >       ~Private();\n> >\n> >       unsigned int numPlanes() const;\n> > @@ -33,35 +35,69 @@ public:\n> >       Span<uint8_t> plane(unsigned int plane);\n> >\n> >       size_t jpegBufferSize(size_t maxJpegBufferSize) const;\n> > +\n> > +private:\n> > +     /* \\todo remove planes_ is added to MappedBuffer. */\n>\n> I'm not sure to follow you. Do you mean \"Remove planes_ when it will be\n> added to MappedBuffer\" maybe ?\n>\n> > +     std::vector<Span<uint8_t>> planes_;\n> >  };\n> >\n> >  CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer,\n> > -                            buffer_handle_t camera3Buffer, int flags)\n> > +                            buffer_handle_t camera3Buffer,\n> > +                            libcamera::PixelFormat pixelFormat,\n> > +                            const libcamera::Size &size, int flags)\n> >  {\n> > -     maps_.reserve(camera3Buffer->numFds);\n> >       error_ = 0;\n> >\n>\n> I'd add a comment here:\n>\n>         /*\n>          * As Android doesn't offer an API to query buffer layouts, assume for\n>          * now that the buffer is backed by a single dmabuf, with planes being\n>          * stored contiguously.\n>          */\n>\n> > +     int fd = -1;\n> >       for (int i = 0; i < camera3Buffer->numFds; i++) {\n> > -             if (camera3Buffer->data[i] == -1)\n> > -                     continue;\n> > -\n> > -             off_t length = lseek(camera3Buffer->data[i], 0, SEEK_END);\n> > -             if (length < 0) {\n> > -                     error_ = -errno;\n> > -                     LOG(HAL, Error) << \"Failed to query plane length\";\n> > +             if (camera3Buffer->data[i] != -1) {\n> > +                     fd = camera3Buffer->data[i];\n> >                       break;\n> >               }\n> > +     }\n>\n> Could we verify the assumption ? Maybe something like\n>\n>         int fd = -1;\n>         for (int i = 0; i < camera3Buffer->numFds; i++) {\n>                 if (camera3Buffer->data[i] == -1 ||\n>                     camera3Buffer->data[i] == fd)\n>                         continue;\n>\n>                 if (fd != -1)\n>                         LOG(HAL, Fatal)\n>                                 << \"Discontiguous planes are not supported\";\n>                 fd = camera3Buffer->data[i];\n>         }\n>\n\nIf numFds > 1, generally the FDs will be different, because the handle\nis sent over Binder, which would create new FDs for each array element\nwhen unflattening the handle on the receiver side.\n\n> >\n> > -             void *address = mmap(nullptr, length, flags, MAP_SHARED,\n> > -                                  camera3Buffer->data[i], 0);\n> > -             if (address == MAP_FAILED) {\n> > -                     error_ = -errno;\n> > -                     LOG(HAL, Error) << \"Failed to mmap plane\";\n> > -                     break;\n> > -             }\n> > +     if (fd != -1) {\n>\n> That should be\n>\n>         if (fd == -1) {\n>\n> > +             error_ = EINVAL;\n>\n> -EINVAL\n>\n> > +             LOG(HAL, Error) << \"No valid file descriptor\";\n> > +             return;\n> > +     }\n> > +\n> > +     const auto &info = libcamera::PixelFormatInfo::info(pixelFormat);\n> > +     if (!info.isValid()) {\n> > +             error_ = EINVAL;\n>\n> -EINVAL\n>\n> > +             LOG(HAL, Error) << \"Invalid pixel format: \"\n> > +                             << pixelFormat.toString();\n> > +             return;\n> > +     }\n> > +\n> > +     size_t bufferLength = lseek(fd, 0, SEEK_END);\n> > +     if (bufferLength < 0) {\n>\n> This comparison is always false, as size_t is unsigned. The correct data\n> type if off_t for the return value of lseek.\n\nIs perhaps the auto type the right thing to use to avoid issues like this?\n\nBest regards,\nTomasz\n\n>\n> > +             error_ = -errno;\n> > +             LOG(HAL, Error) << \"Failed to get buffer length\";\n> > +             return;\n> > +     }\n> > +\n> > +     void *address = mmap(nullptr, bufferLength, flags, MAP_SHARED, fd, 0);\n> > +     if (address == MAP_FAILED) {\n> > +             error_ = -errno;\n> > +             LOG(HAL, Error) << \"Failed to mmap plane\";\n> > +             return;\n> > +     }\n> > +     maps_.emplace_back(static_cast<uint8_t *>(address), bufferLength);\n> > +\n> > +     const unsigned int numPlanes = info.numPlanes();\n> > +     planes_.resize(numPlanes);\n> > +     unsigned int offset = 0;\n> > +     for (unsigned int i = 0; i < numPlanes; ++i) {\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>\n> It may make sense to move this calculation to a new helper function in\n> libcamera::PixelFormatInfo at some point, but it's fine for now.\n>\n> > +\n> > +             planes_[i] = libcamera::Span<uint8_t>(\n> > +                     static_cast<uint8_t *>(address) + offset, planeSize);\n> >\n> > -             maps_.emplace_back(static_cast<uint8_t *>(address),\n> > -                                static_cast<size_t>(length));\n> > +             offset += planeSize;\n> >       }\n> >  }\n> >\n> > @@ -71,15 +107,15 @@ CameraBuffer::Private::~Private()\n> >\n> >  unsigned int CameraBuffer::Private::numPlanes() const\n> >  {\n> > -     return maps_.size();\n> > +     return planes_.size();\n> >  }\n> >\n> >  Span<uint8_t> CameraBuffer::Private::plane(unsigned int plane)\n> >  {\n> > -     if (plane >= maps_.size())\n> > +     if (plane >= planes_.size())\n> >               return {};\n> >\n> > -     return maps_[plane];\n> > +     return planes_[plane];\n> >  }\n> >\n> >  size_t CameraBuffer::Private::jpegBufferSize(size_t maxJpegBufferSize) const\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 E1784BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 Aug 2021 09:33:42 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5F3D06891F;\n\tFri, 27 Aug 2021 11:33:42 +0200 (CEST)","from mail-ej1-x631.google.com (mail-ej1-x631.google.com\n\t[IPv6:2a00:1450:4864:20::631])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 296C268891\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Aug 2021 11:33:40 +0200 (CEST)","by mail-ej1-x631.google.com with SMTP id x11so12641860ejv.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Aug 2021 02:33:40 -0700 (PDT)","from mail-wr1-f50.google.com (mail-wr1-f50.google.com.\n\t[209.85.221.50]) by smtp.gmail.com with ESMTPSA id\n\tw11sm2991213edc.5.2021.08.27.02.33.38\n\tfor <libcamera-devel@lists.libcamera.org>\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tFri, 27 Aug 2021 02:33:38 -0700 (PDT)","by mail-wr1-f50.google.com with SMTP id q11so9400440wrr.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Aug 2021 02:33:38 -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=\"Zsyd3BOO\"; 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=9SEoDBs97NMLK8U9SIJeiMzLdJaKE0Cqmti2GzGW9aw=;\n\tb=Zsyd3BOOFiYYdpiEktcD55GEDy8PRREFvO2OJamxnLcdlWkniqOhX/XFEHYxSWqjQb\n\tK+AeTMs+gwrl+CX4I+lnHm7sGhRCpPM71Vt/mDOKK2gtnOiuQMP5vT9Yx1KupC1SzlE6\n\tTD38rJgAPkqsfuQdyHZcasGqq88pqomD4P45w=","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=9SEoDBs97NMLK8U9SIJeiMzLdJaKE0Cqmti2GzGW9aw=;\n\tb=a4aQnCx2gb/nsYmSSlMJuWyZm9kAUT4OeorkzAD4pzYPwXpOd87SPv4+qdqa4Jss4S\n\t5QoiVbuGE3+x/CV2lo3US5bcMWshUHB1M/V/9uTIQQX3X2FfVPq/rPbocWYNqx3bxNFs\n\tOgpYLnDwwyZPgwoVa/QAIa9XwILv+1G+YGioLbfC7PyPlMZApdY9bg8p0RGnueTqShpY\n\tkArlA+uOK5AGXoTUdn0Yl8oQvfNjj2J5GskOOMdVjj6scF2IHS2XCGvLMvDvnlwTETa6\n\toinwRFxswGlB8DBoMu5VPv90YVEVfJ1jWE/qPZfcsofrTt9KKk423gpyINvCa/jieLxC\n\tJrzA==","X-Gm-Message-State":"AOAM532JKEaeuIUtn6yMAhrYiITpIzPM+2CGzOWyK+6gNzzhtpPp3t8n\n\tcp+QfY5pPHuZQ+Vx4F3PoXWI2xUMgKxIkg==","X-Google-Smtp-Source":"ABdhPJzaYk1BF23BgHLbgotti99oLrJKN8F5ZMKBk4D/jXqlOJ0xK3yO077xHtpkT2VQnRE/6OVnHQ==","X-Received":["by 2002:a17:906:8cc:: with SMTP id\n\to12mr1112561eje.252.1630056819164; \n\tFri, 27 Aug 2021 02:33:39 -0700 (PDT)","by 2002:a5d:538e:: with SMTP id\n\td14mr9159892wrv.192.1630056817477; \n\tFri, 27 Aug 2021 02:33:37 -0700 (PDT)"],"MIME-Version":"1.0","References":"<20210823124321.980847-1-hiroh@chromium.org>\n\t<20210823124321.980847-2-hiroh@chromium.org>\n\t<YSQR1/vsLxiK7UAT@pendragon.ideasonboard.com>","In-Reply-To":"<YSQR1/vsLxiK7UAT@pendragon.ideasonboard.com>","From":"Tomasz Figa <tfiga@chromium.org>","Date":"Fri, 27 Aug 2021 18:33:25 +0900","X-Gmail-Original-Message-ID":"<CAAFQd5D3O2L3eBiq7MqBHpLvV5-kgUoqMvtbZd87AQ16y9b3dQ@mail.gmail.com>","Message-ID":"<CAAFQd5D3O2L3eBiq7MqBHpLvV5-kgUoqMvtbZd87AQ16y9b3dQ@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [RFC PATCH 1/3] android:\n\tgeneric_camera_buffer: Correct buffer mapping","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>"}}]