summaryrefslogtreecommitdiff
path: root/src/v4l2/v4l2_camera.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/v4l2/v4l2_camera.cpp')
-rw-r--r--src/v4l2/v4l2_camera.cpp157
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_;
+}