diff options
Diffstat (limited to 'src/android/mm/generic_camera_buffer.cpp')
-rw-r--r-- | src/android/mm/generic_camera_buffer.cpp | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/android/mm/generic_camera_buffer.cpp b/src/android/mm/generic_camera_buffer.cpp new file mode 100644 index 00000000..0ffcb445 --- /dev/null +++ b/src/android/mm/generic_camera_buffer.cpp @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Google Inc. + * + * Generic Android frame buffer backend + */ + +#include "../camera_buffer.h" + +#include <sys/mman.h> +#include <unistd.h> + +#include <libcamera/base/log.h> + +#include "libcamera/internal/formats.h" +#include "libcamera/internal/mapped_framebuffer.h" + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(HAL) + +class CameraBuffer::Private : public Extensible::Private, + public MappedBuffer +{ + LIBCAMERA_DECLARE_PUBLIC(CameraBuffer) + +public: + Private(CameraBuffer *cameraBuffer, buffer_handle_t camera3Buffer, + PixelFormat pixelFormat, const Size &size, int flags); + ~Private(); + + unsigned int numPlanes() const; + + Span<uint8_t> plane(unsigned int plane); + + unsigned int stride(unsigned int plane) const; + unsigned int offset(unsigned int plane) const; + unsigned int size(unsigned int plane) const; + + size_t jpegBufferSize(size_t maxJpegBufferSize) const; + +private: + struct PlaneInfo { + unsigned int stride; + unsigned int offset; + unsigned int size; + }; + + void map(); + + int fd_; + int flags_; + off_t bufferLength_; + bool mapped_; + std::vector<PlaneInfo> planeInfo_; +}; + +CameraBuffer::Private::Private([[maybe_unused]] CameraBuffer *cameraBuffer, + buffer_handle_t camera3Buffer, + PixelFormat pixelFormat, + const Size &size, int flags) + : fd_(-1), flags_(flags), bufferLength_(-1), mapped_(false) +{ + error_ = 0; + + const auto &info = PixelFormatInfo::info(pixelFormat); + if (!info.isValid()) { + error_ = -EINVAL; + LOG(HAL, Error) << "Invalid pixel format: " << pixelFormat; + return; + } + + /* + * As Android doesn't offer an API to query buffer layouts, assume for + * now that the buffer is backed by a single dmabuf, with planes being + * stored contiguously. + */ + for (int i = 0; i < camera3Buffer->numFds; i++) { + if (camera3Buffer->data[i] == -1 || camera3Buffer->data[i] == fd_) + continue; + + if (fd_ != -1) { + error_ = -EINVAL; + LOG(HAL, Error) << "Discontiguous planes are not supported"; + return; + } + + fd_ = camera3Buffer->data[i]; + } + + if (fd_ == -1) { + error_ = -EINVAL; + LOG(HAL, Error) << "No valid file descriptor"; + return; + } + + bufferLength_ = lseek(fd_, 0, SEEK_END); + if (bufferLength_ < 0) { + error_ = -errno; + LOG(HAL, Error) << "Failed to get buffer length"; + return; + } + + const unsigned int numPlanes = info.numPlanes(); + planeInfo_.resize(numPlanes); + + unsigned int offset = 0; + for (unsigned int i = 0; i < numPlanes; ++i) { + const unsigned int planeSize = info.planeSize(size, i); + + planeInfo_[i].stride = info.stride(size.width, i, 1u); + planeInfo_[i].offset = offset; + planeInfo_[i].size = planeSize; + + if (bufferLength_ < offset + planeSize) { + LOG(HAL, Error) << "Plane " << i << " is out of buffer:" + << " plane offset=" << offset + << ", plane size=" << planeSize + << ", buffer length=" << bufferLength_; + return; + } + + offset += planeSize; + } +} + +CameraBuffer::Private::~Private() +{ +} + +unsigned int CameraBuffer::Private::numPlanes() const +{ + return planeInfo_.size(); +} + +Span<uint8_t> CameraBuffer::Private::plane(unsigned int plane) +{ + if (!mapped_) + map(); + if (!mapped_) + return {}; + + return planes_[plane]; +} + +unsigned int CameraBuffer::Private::stride(unsigned int plane) const +{ + if (plane >= planeInfo_.size()) + return 0; + + return planeInfo_[plane].stride; +} + +unsigned int CameraBuffer::Private::offset(unsigned int plane) const +{ + if (plane >= planeInfo_.size()) + return 0; + + return planeInfo_[plane].offset; +} + +unsigned int CameraBuffer::Private::size(unsigned int plane) const +{ + if (plane >= planeInfo_.size()) + return 0; + + return planeInfo_[plane].size; +} + +size_t CameraBuffer::Private::jpegBufferSize(size_t maxJpegBufferSize) const +{ + ASSERT(bufferLength_ >= 0); + + return std::min<unsigned int>(bufferLength_, maxJpegBufferSize); +} + +void CameraBuffer::Private::map() +{ + ASSERT(fd_ != -1); + ASSERT(bufferLength_ >= 0); + + void *address = mmap(nullptr, bufferLength_, flags_, MAP_SHARED, fd_, 0); + if (address == MAP_FAILED) { + error_ = -errno; + LOG(HAL, Error) << "Failed to mmap plane"; + return; + } + maps_.emplace_back(static_cast<uint8_t *>(address), bufferLength_); + + planes_.reserve(planeInfo_.size()); + for (const auto &info : planeInfo_) { + planes_.emplace_back( + static_cast<uint8_t *>(address) + info.offset, info.size); + } + + mapped_ = true; +} + +PUBLIC_CAMERA_BUFFER_IMPLEMENTATION |