summaryrefslogtreecommitdiff
path: root/src/android/mm/generic_camera_buffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/mm/generic_camera_buffer.cpp')
-rw-r--r--src/android/mm/generic_camera_buffer.cpp199
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