Message ID | 20210906225636.14683-21-laurent.pinchart@ideasonboard.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
On 06/09/2021 23:56, Laurent Pinchart wrote: > The new Image class represents a multi-planar image with direct access > to pixel data. It currently duplicates the function of the > MappedFrameBuffer class which is internal to libcamera, and will serve > as a design playground to improve the API until it is considered ready > to be made part of the libcamera public API. > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > Reviewed-by: Hirokazu Honda <hiroh@chromium.org> > --- > src/cam/image.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++++ > src/cam/image.h | 52 +++++++++++++++++++++ > src/cam/meson.build | 1 + > 3 files changed, 160 insertions(+) > create mode 100644 src/cam/image.cpp > create mode 100644 src/cam/image.h > > diff --git a/src/cam/image.cpp b/src/cam/image.cpp > new file mode 100644 > index 000000000000..7ae5f52dccb4 > --- /dev/null > +++ b/src/cam/image.cpp > @@ -0,0 +1,107 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2021, Google Inc. > + * > + * image.cpp - Multi-planar image with access to pixel data > + */ > + > +#include "image.h" > + > +#include <assert.h> > +#include <errno.h> > +#include <iostream> > +#include <map> > +#include <string.h> > +#include <sys/mman.h> > +#include <unistd.h> > + > +using namespace libcamera; > + > +std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode mode) > +{ > + std::unique_ptr<Image> image{ new Image() }; > + > + assert(!buffer->planes().empty()); > + > + int mmapFlags = 0; > + > + if (mode & MapMode::ReadOnly) > + mmapFlags |= PROT_READ; > + > + if (mode & MapMode::WriteOnly) > + mmapFlags |= PROT_WRITE; > + > + struct MappedBufferInfo { > + uint8_t *address = nullptr; > + size_t mapLength = 0; > + size_t dmabufLength = 0; > + }; > + std::map<int, MappedBufferInfo> mappedBuffers; > + > + for (const FrameBuffer::Plane &plane : buffer->planes()) { > + const int fd = plane.fd.fd(); > + if (mappedBuffers.find(fd) == mappedBuffers.end()) { > + const size_t length = lseek(fd, 0, SEEK_END); > + mappedBuffers[fd] = MappedBufferInfo{ nullptr, 0, length }; > + } > + > + const size_t length = mappedBuffers[fd].dmabufLength; > + > + if (plane.offset > length || > + plane.offset + plane.length > length) { > + std::cerr << "plane is out of buffer: buffer length=" > + << length << ", plane offset=" << plane.offset > + << ", plane length=" << plane.length > + << std::endl; > + return nullptr; > + } > + size_t &mapLength = mappedBuffers[fd].mapLength; > + mapLength = std::max(mapLength, > + static_cast<size_t>(plane.offset + plane.length)); > + } > + > + for (const FrameBuffer::Plane &plane : buffer->planes()) { > + const int fd = plane.fd.fd(); > + auto &info = mappedBuffers[fd]; > + if (!info.address) { > + void *address = mmap(nullptr, info.mapLength, mmapFlags, > + MAP_SHARED, fd, 0); > + if (address == MAP_FAILED) { > + int error = -errno; > + std::cerr << "Failed to mmap plane: " > + << strerror(-error) << std::endl; > + return nullptr; > + } > + > + info.address = static_cast<uint8_t *>(address); > + image->maps_.emplace_back(info.address, info.mapLength); > + } > + > + image->planes_.emplace_back(info.address + plane.offset, plane.length); > + } > + > + return image; > +} > + > +Image::Image() = default; > + > +Image::~Image() > +{ > + for (Span<uint8_t> &map : maps_) > + munmap(map.data(), map.size()); > +} > + > +unsigned int Image::numPlanes() const > +{ > + return planes_.size(); > +} > + > +Span<uint8_t> Image::data(unsigned int plane) > +{ assert(plane < planes_.size()); ? > + return planes_[plane]; > +} > + > +Span<const uint8_t> Image::data(unsigned int plane) const > +{ assert(plane < planes_.size()); ? But other than that, It's good to get something started to handle this properly. Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + return planes_[plane]; > +} > diff --git a/src/cam/image.h b/src/cam/image.h > new file mode 100644 > index 000000000000..1ce5f84e5f9e > --- /dev/null > +++ b/src/cam/image.h > @@ -0,0 +1,52 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2021, Google Inc. > + * > + * image.h - Multi-planar image with access to pixel data > + */ > +#ifndef __CAM_IMAGE_H__ > +#define __CAM_IMAGE_H__ > + > +#include <memory> > +#include <stdint.h> > +#include <vector> > + > +#include <libcamera/base/class.h> > +#include <libcamera/base/flags.h> > +#include <libcamera/base/span.h> > + > +#include <libcamera/framebuffer.h> > + > +class Image > +{ > +public: > + enum class MapMode { > + ReadOnly = 1 << 0, > + WriteOnly = 1 << 1, > + ReadWrite = ReadOnly | WriteOnly, > + }; > + > + static std::unique_ptr<Image> fromFrameBuffer(const libcamera::FrameBuffer *buffer, > + MapMode mode); > + > + ~Image(); > + > + unsigned int numPlanes() const; > + > + libcamera::Span<uint8_t> data(unsigned int plane); > + libcamera::Span<const uint8_t> data(unsigned int plane) const; > + > +private: > + LIBCAMERA_DISABLE_COPY(Image) > + > + Image(); > + > + std::vector<libcamera::Span<uint8_t>> maps_; > + std::vector<libcamera::Span<uint8_t>> planes_; > +}; > + > +namespace libcamera { > +LIBCAMERA_FLAGS_ENABLE_OPERATORS(Image::MapMode) > +} > + > +#endif /* __CAM_IMAGE_H__ */ > diff --git a/src/cam/meson.build b/src/cam/meson.build > index ea36aaa5c514..e8e2ae57d3f4 100644 > --- a/src/cam/meson.build > +++ b/src/cam/meson.build > @@ -14,6 +14,7 @@ cam_sources = files([ > 'event_loop.cpp', > 'file_sink.cpp', > 'frame_sink.cpp', > + 'image.cpp', > 'main.cpp', > 'options.cpp', > 'stream_options.cpp', >
diff --git a/src/cam/image.cpp b/src/cam/image.cpp new file mode 100644 index 000000000000..7ae5f52dccb4 --- /dev/null +++ b/src/cam/image.cpp @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * image.cpp - Multi-planar image with access to pixel data + */ + +#include "image.h" + +#include <assert.h> +#include <errno.h> +#include <iostream> +#include <map> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +using namespace libcamera; + +std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode mode) +{ + std::unique_ptr<Image> image{ new Image() }; + + assert(!buffer->planes().empty()); + + int mmapFlags = 0; + + if (mode & MapMode::ReadOnly) + mmapFlags |= PROT_READ; + + if (mode & MapMode::WriteOnly) + mmapFlags |= PROT_WRITE; + + struct MappedBufferInfo { + uint8_t *address = nullptr; + size_t mapLength = 0; + size_t dmabufLength = 0; + }; + std::map<int, MappedBufferInfo> mappedBuffers; + + for (const FrameBuffer::Plane &plane : buffer->planes()) { + const int fd = plane.fd.fd(); + if (mappedBuffers.find(fd) == mappedBuffers.end()) { + const size_t length = lseek(fd, 0, SEEK_END); + mappedBuffers[fd] = MappedBufferInfo{ nullptr, 0, length }; + } + + const size_t length = mappedBuffers[fd].dmabufLength; + + if (plane.offset > length || + plane.offset + plane.length > length) { + std::cerr << "plane is out of buffer: buffer length=" + << length << ", plane offset=" << plane.offset + << ", plane length=" << plane.length + << std::endl; + return nullptr; + } + size_t &mapLength = mappedBuffers[fd].mapLength; + mapLength = std::max(mapLength, + static_cast<size_t>(plane.offset + plane.length)); + } + + for (const FrameBuffer::Plane &plane : buffer->planes()) { + const int fd = plane.fd.fd(); + auto &info = mappedBuffers[fd]; + if (!info.address) { + void *address = mmap(nullptr, info.mapLength, mmapFlags, + MAP_SHARED, fd, 0); + if (address == MAP_FAILED) { + int error = -errno; + std::cerr << "Failed to mmap plane: " + << strerror(-error) << std::endl; + return nullptr; + } + + info.address = static_cast<uint8_t *>(address); + image->maps_.emplace_back(info.address, info.mapLength); + } + + image->planes_.emplace_back(info.address + plane.offset, plane.length); + } + + return image; +} + +Image::Image() = default; + +Image::~Image() +{ + for (Span<uint8_t> &map : maps_) + munmap(map.data(), map.size()); +} + +unsigned int Image::numPlanes() const +{ + return planes_.size(); +} + +Span<uint8_t> Image::data(unsigned int plane) +{ + return planes_[plane]; +} + +Span<const uint8_t> Image::data(unsigned int plane) const +{ + return planes_[plane]; +} diff --git a/src/cam/image.h b/src/cam/image.h new file mode 100644 index 000000000000..1ce5f84e5f9e --- /dev/null +++ b/src/cam/image.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * image.h - Multi-planar image with access to pixel data + */ +#ifndef __CAM_IMAGE_H__ +#define __CAM_IMAGE_H__ + +#include <memory> +#include <stdint.h> +#include <vector> + +#include <libcamera/base/class.h> +#include <libcamera/base/flags.h> +#include <libcamera/base/span.h> + +#include <libcamera/framebuffer.h> + +class Image +{ +public: + enum class MapMode { + ReadOnly = 1 << 0, + WriteOnly = 1 << 1, + ReadWrite = ReadOnly | WriteOnly, + }; + + static std::unique_ptr<Image> fromFrameBuffer(const libcamera::FrameBuffer *buffer, + MapMode mode); + + ~Image(); + + unsigned int numPlanes() const; + + libcamera::Span<uint8_t> data(unsigned int plane); + libcamera::Span<const uint8_t> data(unsigned int plane) const; + +private: + LIBCAMERA_DISABLE_COPY(Image) + + Image(); + + std::vector<libcamera::Span<uint8_t>> maps_; + std::vector<libcamera::Span<uint8_t>> planes_; +}; + +namespace libcamera { +LIBCAMERA_FLAGS_ENABLE_OPERATORS(Image::MapMode) +} + +#endif /* __CAM_IMAGE_H__ */ diff --git a/src/cam/meson.build b/src/cam/meson.build index ea36aaa5c514..e8e2ae57d3f4 100644 --- a/src/cam/meson.build +++ b/src/cam/meson.build @@ -14,6 +14,7 @@ cam_sources = files([ 'event_loop.cpp', 'file_sink.cpp', 'frame_sink.cpp', + 'image.cpp', 'main.cpp', 'options.cpp', 'stream_options.cpp',