diff options
Diffstat (limited to 'src/v4l2/v4l2_camera.cpp')
-rw-r--r-- | src/v4l2/v4l2_camera.cpp | 157 |
1 files changed, 125 insertions, 32 deletions
diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index ecbb70ac..94d138cd 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -1,22 +1,26 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019, Google Inc. * - * v4l2_camera.cpp - V4L2 compatibility camera + * V4L2 compatibility camera */ #include "v4l2_camera.h" #include <errno.h> +#include <unistd.h> -#include "log.h" +#include <libcamera/base/log.h> + +#include <libcamera/control_ids.h> using namespace libcamera; -LOG_DECLARE_CATEGORY(V4L2Compat); +LOG_DECLARE_CATEGORY(V4L2Compat) V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera) - : camera_(camera), isRunning_(false), bufferAllocator_(nullptr) + : camera_(camera), controls_(controls::controls), isRunning_(false), + bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0) { camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete); } @@ -26,9 +30,8 @@ V4L2Camera::~V4L2Camera() close(); } -int V4L2Camera::open() +int V4L2Camera::open(StreamConfiguration *streamConfig) { - /* \todo Support multiple open. */ if (camera_->acquire() < 0) { LOG(V4L2Compat, Error) << "Failed to acquire camera"; return -EINVAL; @@ -42,31 +45,38 @@ int V4L2Camera::open() bufferAllocator_ = new FrameBufferAllocator(camera_); + *streamConfig = config_->at(0); return 0; } void V4L2Camera::close() { + requestPool_.clear(); + delete bufferAllocator_; bufferAllocator_ = nullptr; camera_->release(); } -void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig) +void V4L2Camera::bind(int efd) { - *streamConfig = config_->at(0); + efd_ = efd; +} + +void V4L2Camera::unbind() +{ + efd_ = -1; } std::vector<V4L2Camera::Buffer> V4L2Camera::completedBuffers() { std::vector<Buffer> v; - bufferLock_.lock(); + MutexLocker lock(bufferLock_); for (std::unique_ptr<Buffer> &metadata : completedBuffers_) v.push_back(*metadata.get()); completedBuffers_.clear(); - bufferLock_.unlock(); return v; } @@ -84,7 +94,17 @@ void V4L2Camera::requestComplete(Request *request) completedBuffers_.push_back(std::move(metadata)); bufferLock_.unlock(); - bufferSema_.release(); + uint64_t data = 1; + int ret = ::write(efd_, &data, sizeof(data)); + if (ret != sizeof(data)) + LOG(V4L2Compat, Error) << "Failed to signal eventfd POLLIN"; + + request->reuse(); + { + MutexLocker locker(bufferMutex_); + bufferAvailableCount_++; + } + bufferCV_.notify_all(); } int V4L2Camera::configure(StreamConfiguration *streamConfigOut, @@ -118,29 +138,65 @@ int V4L2Camera::configure(StreamConfiguration *streamConfigOut, return 0; } +int V4L2Camera::validateConfiguration(const PixelFormat &pixelFormat, + const Size &size, + StreamConfiguration *streamConfigOut) +{ + std::unique_ptr<CameraConfiguration> config = + camera_->generateConfiguration({ StreamRole::Viewfinder }); + StreamConfiguration &cfg = config->at(0); + cfg.size = size; + cfg.pixelFormat = pixelFormat; + cfg.bufferCount = 1; + + CameraConfiguration::Status validation = config->validate(); + if (validation == CameraConfiguration::Invalid) + return -EINVAL; + + *streamConfigOut = cfg; + + return 0; +} + int V4L2Camera::allocBuffers(unsigned int count) { - Stream *stream = *camera_->streams().begin(); + Stream *stream = config_->at(0).stream(); - return bufferAllocator_->allocate(stream); + int ret = bufferAllocator_->allocate(stream); + if (ret < 0) + return ret; + + for (unsigned int i = 0; i < count; i++) { + std::unique_ptr<Request> request = camera_->createRequest(i); + if (!request) { + requestPool_.clear(); + return -ENOMEM; + } + requestPool_.push_back(std::move(request)); + } + + return ret; } void V4L2Camera::freeBuffers() { - Stream *stream = *camera_->streams().begin(); + pendingRequests_.clear(); + requestPool_.clear(); + + Stream *stream = config_->at(0).stream(); bufferAllocator_->free(stream); } -FileDescriptor V4L2Camera::getBufferFd(unsigned int index) +int V4L2Camera::getBufferFd(unsigned int index) { - Stream *stream = *camera_->streams().begin(); + Stream *stream = config_->at(0).stream(); const std::vector<std::unique_ptr<FrameBuffer>> &buffers = bufferAllocator_->buffers(stream); if (buffers.size() <= index) - return FileDescriptor(); + return -1; - return buffers[index]->planes()[0].fd; + return buffers[index]->planes()[0].fd.get(); } int V4L2Camera::streamOn() @@ -148,15 +204,17 @@ int V4L2Camera::streamOn() if (isRunning_) return 0; - int ret = camera_->start(); + int ret = camera_->start(&controls_); if (ret < 0) return ret == -EACCES ? -EBUSY : ret; + controls_.clear(); + isRunning_ = true; - for (std::unique_ptr<Request> &req : pendingRequests_) { + for (Request *req : pendingRequests_) { /* \todo What should we do if this returns -EINVAL? */ - ret = camera_->queueRequest(req.release()); + ret = camera_->queueRequest(req); if (ret < 0) return ret == -EACCES ? -EBUSY : ret; } @@ -168,27 +226,35 @@ int V4L2Camera::streamOn() int V4L2Camera::streamOff() { - /* \todo Restore buffers to reqbufs state? */ - if (!isRunning_) + if (!isRunning_) { + for (std::unique_ptr<Request> &req : requestPool_) + req->reuse(); + return 0; + } + + pendingRequests_.clear(); int ret = camera_->stop(); if (ret < 0) return ret == -EACCES ? -EBUSY : ret; - isRunning_ = false; + { + MutexLocker locker(bufferMutex_); + isRunning_ = false; + } + bufferCV_.notify_all(); return 0; } int V4L2Camera::qbuf(unsigned int index) { - std::unique_ptr<Request> request = - std::unique_ptr<Request>(camera_->createRequest(index)); - if (!request) { - LOG(V4L2Compat, Error) << "Can't create request"; - return -ENOMEM; + if (index >= requestPool_.size()) { + LOG(V4L2Compat, Error) << "Invalid index"; + return -EINVAL; } + Request *request = requestPool_[index].get(); Stream *stream = config_->at(0).stream(); FrameBuffer *buffer = bufferAllocator_->buffers(stream)[index].get(); @@ -199,11 +265,13 @@ int V4L2Camera::qbuf(unsigned int index) } if (!isRunning_) { - pendingRequests_.push_back(std::move(request)); + pendingRequests_.push_back(request); return 0; } - ret = camera_->queueRequest(request.release()); + request->controls().merge(std::move(controls_)); + + ret = camera_->queueRequest(request); if (ret < 0) { LOG(V4L2Compat, Error) << "Can't queue request"; return ret == -EACCES ? -EBUSY : ret; @@ -211,3 +279,28 @@ int V4L2Camera::qbuf(unsigned int index) return 0; } + +void V4L2Camera::waitForBufferAvailable() +{ + MutexLocker locker(bufferMutex_); + bufferCV_.wait(locker, [&]() LIBCAMERA_TSA_REQUIRES(bufferMutex_) { + return bufferAvailableCount_ >= 1 || !isRunning_; + }); + if (isRunning_) + bufferAvailableCount_--; +} + +bool V4L2Camera::isBufferAvailable() +{ + MutexLocker locker(bufferMutex_); + if (bufferAvailableCount_ < 1) + return false; + + bufferAvailableCount_--; + return true; +} + +bool V4L2Camera::isRunning() +{ + return isRunning_; +} |