| /* 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.get(); |
| 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.get(); |
| 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]; |
| } |