[libcamera-devel,v3,21/30] cam: Add Image class
diff mbox series

Message ID 20210906225636.14683-21-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: Handle fallout of FrameBuffer offset support
Related show

Commit Message

Laurent Pinchart Sept. 6, 2021, 10:56 p.m. UTC
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

Comments

Kieran Bingham Sept. 7, 2021, 11:27 a.m. UTC | #1
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',
>

Patch
diff mbox series

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',