/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2021, Google Inc. * * generic_camera_buffer.cpp - 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.toString(); 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