blob: a4349f89cb13d00bec3b22d6aaefa6a518a2d100 [file] [log] [blame]
/* 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