[PATCH 5/5] libcamera: Add InfoFrame implememtation

Harvey Yang chenghaoyang at chromium.org
Wed Oct 9 09:41:23 CEST 2024


InfoFrame consists of an InfoFrame class which is used as an extension
of FrameBuffer, with extra information of pixel format, size, and memory
address if it's mmapped.

InfoFramePool is introduced to handle InfoFrames more easily, which supports
a pool implementation of InfoFrames, creates frames by DmaBufAllocator, and
mmap/munmap all frames of the pool.

Signed-off-by: Han-Lin Chen <hanlinchen at chromium.org>
Co-developed-by: Harvey Yang <chenghaoyang at chromium.org>
Signed-off-by: Harvey Yang <chenghaoyang at chromium.org>
---
 include/libcamera/internal/info_frame.h | 105 ++++++++
 include/libcamera/internal/meson.build  |   1 +
 src/libcamera/info_frame.cpp            | 302 ++++++++++++++++++++++++
 src/libcamera/meson.build               |   1 +
 4 files changed, 409 insertions(+)
 create mode 100644 include/libcamera/internal/info_frame.h
 create mode 100644 src/libcamera/info_frame.cpp

diff --git a/include/libcamera/internal/info_frame.h b/include/libcamera/internal/info_frame.h
new file mode 100644
index 000000000..29b445bb3
--- /dev/null
+++ b/include/libcamera/internal/info_frame.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023, Google Inc.
+ *
+ * info_frame.h - InfoFrame and InfoFramePool
+ */
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include <libcamera/framebuffer.h>
+#include <libcamera/geometry.h>
+#include <libcamera/pixel_format.h>
+
+#include "libcamera/internal/dma_buf_allocator.h"
+#include "libcamera/internal/mailbox.h"
+#include "libcamera/internal/pool.h"
+
+namespace libcamera {
+
+class InfoFrame
+{
+public:
+	struct Plane {
+		uint8_t *address;
+	};
+
+	InfoFrame();
+	InfoFrame(const PixelFormat &format, const Size &size, FrameBuffer *buffers,
+		  unsigned int strideAlign = 1, unsigned int scanAlign = 1,
+		  std::array<Plane, 3> planes = {});
+
+	uint8_t *address(unsigned int plane) const;
+
+	PixelFormat format() const { return format_; }
+	Size size() const { return size_; }
+	FrameBuffer *buffer() const { return buffer_; }
+	unsigned int numPlanes() const { return buffer_ ? buffer_->planes().size() : 0; }
+	unsigned int strideAlign() const { return strideAlign_; }
+	unsigned int scanAlign() const { return scanAlign_; }
+
+private:
+	Size size_;
+	PixelFormat format_;
+	FrameBuffer *buffer_ = nullptr;
+
+	unsigned int strideAlign_ = 1;
+	unsigned int scanAlign_ = 1;
+
+	std::array<Plane, 3> planes_;
+};
+
+class InfoFramePool
+{
+public:
+	struct MappedBufferInfo {
+		uint8_t *address = nullptr;
+		size_t dmabufLength = 0;
+	};
+
+	InfoFramePool();
+	~InfoFramePool();
+
+	int createBuffers(DmaBufAllocator *dmaHeap, const PixelFormat &format,
+			  const Size &size, uint32_t count,
+			  unsigned int strideAlign = 1, unsigned scanAlign = 1);
+
+	void release();
+
+	void fetch(SharedMailBox<InfoFrame> &mailBox);
+
+	int mmap();
+	int munmap();
+
+	bool mapped() const { return 0 != mappedBuffers_.size(); }
+
+	size_t size() { return pool_.size(); }
+	std::vector<std::unique_ptr<FrameBuffer>> &content()
+	{
+		return pool_.content();
+	}
+
+private:
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(InfoFramePool)
+
+	void setBuffers(const PixelFormat &format, const Size &size,
+			std::vector<std::unique_ptr<FrameBuffer>> &buffers,
+			unsigned int align, unsigned int scanAlign);
+
+	InfoFrame get();
+	void put(InfoFrame &frameInfo);
+
+	Size size_;
+	PixelFormat format_;
+	Pool<FrameBuffer *, std::unique_ptr<FrameBuffer>> pool_;
+	unsigned int strideAlign_;
+	unsigned int scanAlign_;
+
+	std::unordered_map<int, MappedBufferInfo> mappedBuffers_;
+};
+
+} /* namespace libcamera */
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index b78e51d0b..8983bcd21 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -21,6 +21,7 @@ libcamera_internal_headers = files([
     'dma_buf_allocator.h',
     'formats.h',
     'framebuffer.h',
+    'info_frame.h',
     'ipa_data_serializer.h',
     'ipa_manager.h',
     'ipa_module.h',
diff --git a/src/libcamera/info_frame.cpp b/src/libcamera/info_frame.cpp
new file mode 100644
index 000000000..5407fadd9
--- /dev/null
+++ b/src/libcamera/info_frame.cpp
@@ -0,0 +1,302 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2023, Google Inc.
+ *
+ * info_frame.cpp - InfoFrame and InfoFramePool
+ */
+
+#include "libcamera/internal/info_frame.h"
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "libcamera/internal/dma_buf_allocator.h"
+#include "libcamera/internal/formats.h"
+#include "libcamera/internal/pool.h"
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(InfoFrame)
+
+/**
+ * \class InfoFrame
+ * \brief A wrapper class that consists of a FrameBuffer and extra information,
+ * including PixelFormat, size, and memory address if it's mmap'ed.
+ */
+
+/**
+ * \class InfoFrame::Plane
+ * \brief Per-plane frame mmap'ed address
+ *
+ * Frames are stored in memory in one or multiple planes. The
+ * InfoFrame::Plane structure stores per-plane mmap'ed address.
+ */
+
+/**
+ * \var InfoFrame::Plane::address
+ * \brief mmap'ed address of a plane
+ */
+
+InfoFrame::InfoFrame() = default;
+
+/**
+ * \brief Create an InfoFrame instance
+ * \param[in] format PixelFormat of the frame
+ * \param[in] size Size of the frame
+ * \param[in] buffer Pointer to the buffer. InfoFrame doesn't take the ownership
+ * \param[in] strideAlign The stride alignment, in bytes (1 for default alignment)
+ * \param[in] scanAlign The scanline alignment, in bytes (1 for default alignment)
+ * \param[in] planes mmap'ed addresses of planes
+ */
+InfoFrame::InfoFrame(const PixelFormat &format, const Size &size,
+		     FrameBuffer *buffer, unsigned int strideAlign,
+		     unsigned int scanAlign, std::array<Plane, 3> planes)
+	: size_(size), format_(format), buffer_(buffer),
+	  strideAlign_(strideAlign), scanAlign_(scanAlign),
+	  planes_(planes)
+
+{
+}
+
+/**
+ * \brief Get the mmap'ed address of \a plane
+ * \param[in] plane The \a plane'th plane
+ * \return Return the mmap'ed address of the plane
+ */
+uint8_t *InfoFrame::address(unsigned int plane) const
+{
+	return (plane < numPlanes()) ? planes_[plane].address : nullptr;
+}
+
+/**
+ * \fn InfoFrame::format()
+ * \return Return the PixelFormat of the frame
+ */
+
+/**
+ * \fn InfoFrame::size()
+ * \return Return the size of the frame
+ */
+
+/**
+ * \fn InfoFrame::buffer()
+ * \return Return the pointer to the buffer
+ */
+
+/**
+ * \fn InfoFrame::numPlanes()
+ * \return Return the number of planes of the buffer
+ */
+
+/**
+ * \fn InfoFrame::strideAlign()
+ * \return Return the stride alignment, in bytes (1 for default alignment)
+ */
+
+/**
+ * \fn InfoFrame::scanAlign()
+ * \return Return the scanline alignment, in bytes (1 for default alignment)
+ */
+
+/**
+ * \class InfoFramePool
+ * \brief A buffer pool that allows users to allocate and request InfoFrame
+ * buffers from DmaBufAllocator
+ */
+
+/**
+ * \struct InfoFramePool::MappedBufferInfo
+ * \brief Contains the information of the mmap'ed buffers, including address
+ * and length
+ */
+
+/**
+ * \var InfoFramePool::MappedBufferInfo::address
+ * \brief mmap'ed address of a buffer
+ */
+
+/**
+ * \var InfoFramePool::MappedBufferInfo::dmabufLength
+ * \brief Length of the DMA buffer
+ */
+
+InfoFramePool::InfoFramePool() = default;
+
+InfoFramePool::~InfoFramePool()
+{
+	release();
+}
+
+/**
+ * \brief Create DMA buffers
+ * \param[in] dmaHeap The DmaBufAllocator to allocate buffers from
+ * \param[in] format PixelFormat of each frame
+ * \param[in] size Size of each frame
+ * \param[in] count Number of frames to create
+ * \param[in] strideAlign The stride alignment, in bytes (1 for default alignment)
+ * \param[in] scanAlign The scanline alignment, in bytes (1 for default alignment)
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int InfoFramePool::createBuffers(DmaBufAllocator *dmaHeap,
+				 const PixelFormat &format, const Size &size,
+				 unsigned int count,
+				 unsigned int strideAlign, unsigned scanAlign)
+{
+	release();
+
+	const PixelFormatInfo &info = PixelFormatInfo::info(format);
+	uint32_t frameSize = info.frameSize(size, strideAlign, scanAlign);
+
+	std::vector<std::unique_ptr<FrameBuffer>> buffers;
+	buffers.reserve(count);
+	for (unsigned int i = 0; i < count; i++) {
+		SharedFD fd(dmaHeap->alloc(("frame-" + std::to_string(i)).c_str(),
+					   frameSize));
+		if (!fd.isValid()) {
+			buffers.clear();
+			return -EBUSY;
+		}
+
+		uint32_t offset = 0;
+		std::vector<FrameBuffer::Plane> planes;
+
+		for (unsigned int j = 0; j < info.numPlanes(); j++) {
+			planes.emplace_back(FrameBuffer::Plane{
+				fd, offset,
+				info.planeSize(size, j, strideAlign, scanAlign),
+				info.stride(size.width, j, strideAlign) });
+			offset += planes.back().length;
+		}
+
+		buffers.emplace_back(std::make_unique<FrameBuffer>(planes));
+	}
+
+	setBuffers(format, size, buffers, strideAlign, scanAlign);
+
+	return 0;
+}
+
+/**
+ * \brief Release all allocated buffers. InfoFramePool can create another set
+ * of buffers again.
+ */
+void InfoFramePool::release()
+{
+	if (munmap())
+		LOG(InfoFrame, Error) << "Failed to unmap mapped buffers";
+
+	pool_.release();
+}
+
+/**
+ * \brief Fetch an available InfoFrame from the pool
+ * \param[out] mailBox The shared pointer of MailBox to store the fetched
+ * InfoFrame
+ *
+ * When \a mailBox is reset, the recycler will return the InfoFrame back to the
+ * InfoFramePool.
+ */
+void InfoFramePool::fetch(SharedMailBox<InfoFrame> &mailBox)
+{
+	auto recycler = [this](InfoFrame &info) {
+		this->put(info);
+	};
+
+	mailBox->put(get(), recycler);
+}
+
+/**
+ * \brief mmap all allocated buffers and set the addresses in `InfoFrame`s
+ */
+int InfoFramePool::mmap()
+{
+	if (!mappedBuffers_.empty())
+		return 0;
+
+	for (auto &buffer : pool_.content()) {
+		for (const FrameBuffer::Plane &plane : buffer->planes()) {
+			const int fd = plane.fd.get();
+			if (mappedBuffers_.find(fd) == mappedBuffers_.end()) {
+				const size_t length = lseek(fd, 0, SEEK_END);
+				mappedBuffers_[fd] = MappedBufferInfo{ nullptr, length };
+			}
+		}
+	}
+
+	for (auto &[fd, info] : mappedBuffers_) {
+		void *address = ::mmap(nullptr, info.dmabufLength,
+				       PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+		info.address = static_cast<uint8_t *>(address);
+	}
+	return 0;
+}
+
+/**
+ * \brief munmap all allocated buffers and reset addresses
+ */
+int InfoFramePool::munmap()
+{
+	if (mappedBuffers_.empty())
+		return 0;
+
+	for (auto &[_, info] : mappedBuffers_) {
+		::munmap(info.address, info.dmabufLength);
+		info.address = nullptr;
+	}
+
+	mappedBuffers_.clear();
+	return 0;
+}
+
+/**
+ * \fn InfoFramePool::mapped()
+ * \return True if mmap() was called on the allocated buffers, false otherwise
+ */
+
+/**
+ * \fn InfoFramePool::size()
+ * \return Return the number of `InfoFrame`s allocated
+ */
+
+/**
+ * \fn InfoFramePool::content()
+ * \return Return frame buffers allocated in the pool
+ */
+
+void InfoFramePool::setBuffers(const PixelFormat &format, const Size &size,
+			       std::vector<std::unique_ptr<FrameBuffer>> &buffers,
+			       unsigned int strideAlign, unsigned int scanAlign)
+{
+	if (munmap())
+		LOG(InfoFrame, Error) << "Failed to unmap buffers";
+
+	size_ = size;
+	format_ = format;
+	pool_.setData(buffers);
+	strideAlign_ = strideAlign;
+	scanAlign_ = scanAlign;
+}
+
+InfoFrame InfoFramePool::get()
+{
+	FrameBuffer *buffer = pool_.get();
+
+	std::array<InfoFrame::Plane, 3> planes;
+	for (size_t i = 0; i < buffer->planes().size() && i < 3; i++) {
+		const int fd = buffer->planes()[i].fd.get();
+		const unsigned int offset = buffer->planes()[i].offset;
+
+		if (mappedBuffers_.count(fd))
+			planes[i].address = mappedBuffers_[fd].address + offset;
+	}
+
+	return InfoFrame(format_, size_, buffer, strideAlign_, scanAlign_, planes);
+}
+
+void InfoFramePool::put(InfoFrame &info)
+{
+	pool_.put(info.buffer());
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index f4403687a..39483b458 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -29,6 +29,7 @@ libcamera_internal_sources = files([
     'device_enumerator_sysfs.cpp',
     'dma_buf_allocator.cpp',
     'formats.cpp',
+    'info_frame.cpp',
     'ipa_controls.cpp',
     'ipa_data_serializer.cpp',
     'ipa_interface.cpp',
-- 
2.47.0.rc0.187.ge670bccf7e-goog



More information about the libcamera-devel mailing list