diff options
-rw-r--r-- | src/cam/image.cpp | 109 | ||||
-rw-r--r-- | src/cam/image.h | 52 | ||||
-rw-r--r-- | src/cam/meson.build | 1 |
3 files changed, 162 insertions, 0 deletions
diff --git a/src/cam/image.cpp b/src/cam/image.cpp new file mode 100644 index 00000000..0180be78 --- /dev/null +++ b/src/cam/image.cpp @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Ideas on Board Oy + * + * 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()); + return planes_[plane]; +} diff --git a/src/cam/image.h b/src/cam/image.h new file mode 100644 index 00000000..f7fc5b27 --- /dev/null +++ b/src/cam/image.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Ideas on Board Oy + * + * 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 ea36aaa5..e8e2ae57 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', |