summaryrefslogtreecommitdiff
path: root/src/v4l2/v4l2_camera.cpp
diff options
context:
space:
mode:
authorPaul Elder <paul.elder@ideasonboard.com>2019-12-06 04:14:52 -0500
committerPaul Elder <paul.elder@ideasonboard.com>2020-01-03 19:53:20 -0500
commit0ce8f2390b5280887a2ac179faf2aa01604cb1cc (patch)
treeea8d04e9013b87217bd8266eab5356c03ded19fa /src/v4l2/v4l2_camera.cpp
parenta3b80f3af87198b97f11f0694ff4d7ddde63fc30 (diff)
v4l2: v4l2_compat: Add V4L2 compatibility layer
Add libcamera V4L2 compatibility layer. This initial implementation supports the minimal set of V4L2 operations, which allows getting, setting, and enumerating formats, and streaming frames from a video device. Some data about the wrapped V4L2 video device are hardcoded. Add a build option named 'v4l2' and adjust the build system to selectively compile the V4L2 compatibility layer. For now we match the V4L2 device node to a libcamera camera based on a devnum that a pipeline handler may optionally map to a libcamera camera. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'src/v4l2/v4l2_camera.cpp')
-rw-r--r--src/v4l2/v4l2_camera.cpp224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
new file mode 100644
index 00000000..3590730f
--- /dev/null
+++ b/src/v4l2/v4l2_camera.cpp
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * v4l2_camera.cpp - V4L2 compatibility camera
+ */
+
+#include "v4l2_camera.h"
+
+#include <errno.h>
+
+#include "log.h"
+#include "utils.h"
+
+using namespace libcamera;
+
+LOG_DECLARE_CATEGORY(V4L2Compat);
+
+FrameMetadata::FrameMetadata(Buffer *buffer)
+ : index_(buffer->index()), bytesused_(buffer->bytesused()),
+ timestamp_(buffer->timestamp()), sequence_(buffer->sequence()),
+ status_(buffer->status())
+{
+}
+
+V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
+ : camera_(camera), isRunning_(false)
+{
+ camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
+}
+
+V4L2Camera::~V4L2Camera()
+{
+ camera_->release();
+}
+
+void V4L2Camera::open(int *ret)
+{
+ /* \todo Support multiple open. */
+ if (camera_->acquire() < 0) {
+ LOG(V4L2Compat, Error) << "Failed to acquire camera";
+ *ret = -EINVAL;
+ return;
+ }
+
+ config_ = camera_->generateConfiguration({ StreamRole::Viewfinder });
+ if (!config_) {
+ camera_->release();
+ *ret = -EINVAL;
+ return;
+ }
+
+ *ret = 0;
+}
+
+void V4L2Camera::close()
+{
+ camera_->release();
+}
+
+void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)
+{
+ *streamConfig = config_->at(0);
+}
+
+std::vector<FrameMetadata> V4L2Camera::completedBuffers()
+{
+ std::vector<FrameMetadata> v;
+
+ bufferLock_.lock();
+ for (std::unique_ptr<FrameMetadata> &metadata : completedBuffers_)
+ v.push_back(*metadata.get());
+ completedBuffers_.clear();
+ bufferLock_.unlock();
+
+ return v;
+}
+
+void V4L2Camera::requestComplete(Request *request)
+{
+ if (request->status() == Request::RequestCancelled)
+ return;
+
+ /* We only have one stream at the moment. */
+ bufferLock_.lock();
+ Buffer *buffer = request->buffers().begin()->second;
+ std::unique_ptr<FrameMetadata> metadata =
+ utils::make_unique<FrameMetadata>(buffer);
+ completedBuffers_.push_back(std::move(metadata));
+ bufferLock_.unlock();
+
+ bufferSema_.release();
+}
+
+void V4L2Camera::configure(int *ret, StreamConfiguration *streamConfigOut,
+ const Size &size, PixelFormat pixelformat,
+ unsigned int bufferCount)
+{
+ StreamConfiguration &streamConfig = config_->at(0);
+ streamConfig.size.width = size.width;
+ streamConfig.size.height = size.height;
+ streamConfig.pixelFormat = pixelformat;
+ streamConfig.bufferCount = bufferCount;
+ /* \todo memoryType (interval vs external) */
+
+ CameraConfiguration::Status validation = config_->validate();
+ if (validation == CameraConfiguration::Invalid) {
+ LOG(V4L2Compat, Debug) << "Configuration invalid";
+ *ret = -EINVAL;
+ return;
+ }
+ if (validation == CameraConfiguration::Adjusted)
+ LOG(V4L2Compat, Debug) << "Configuration adjusted";
+
+ LOG(V4L2Compat, Debug) << "Validated configuration is: "
+ << streamConfig.toString();
+
+ *ret = camera_->configure(config_.get());
+ if (*ret < 0)
+ return;
+
+ *streamConfigOut = config_->at(0);
+}
+
+void V4L2Camera::mmap(void **ret, unsigned int index)
+{
+ Stream *stream = *camera_->streams().begin();
+ *ret = stream->buffers()[index].planes()[0].mem();
+}
+
+void V4L2Camera::allocBuffers(int *ret, unsigned int count)
+{
+ *ret = camera_->allocateBuffers();
+ if (*ret == -EACCES)
+ *ret = -EBUSY;
+}
+
+void V4L2Camera::freeBuffers()
+{
+ camera_->freeBuffers();
+}
+
+void V4L2Camera::streamOn(int *ret)
+{
+ *ret = 0;
+
+ if (isRunning_)
+ return;
+
+ *ret = camera_->start();
+ if (*ret < 0) {
+ if (*ret == -EACCES)
+ *ret = -EBUSY;
+ return;
+ }
+ isRunning_ = true;
+
+ for (std::unique_ptr<Request> &req : pendingRequests_) {
+ /* \todo What should we do if this returns -EINVAL? */
+ *ret = camera_->queueRequest(req.release());
+ if (*ret < 0) {
+ if (*ret == -EACCES)
+ *ret = -EBUSY;
+ return;
+ }
+ }
+
+ pendingRequests_.clear();
+}
+
+void V4L2Camera::streamOff(int *ret)
+{
+ *ret = 0;
+
+ /* \todo Restore buffers to reqbufs state? */
+ if (!isRunning_)
+ return;
+
+ *ret = camera_->stop();
+ if (*ret < 0) {
+ if (*ret == -EACCES)
+ *ret = -EBUSY;
+ return;
+ }
+ isRunning_ = false;
+}
+
+void V4L2Camera::qbuf(int *ret, unsigned int index)
+{
+ Stream *stream = config_->at(0).stream();
+ std::unique_ptr<Buffer> buffer = stream->createBuffer(index);
+ if (!buffer) {
+ LOG(V4L2Compat, Error) << "Can't create buffer";
+ *ret = -ENOMEM;
+ return;
+ }
+
+ std::unique_ptr<Request> request =
+ std::unique_ptr<Request>(camera_->createRequest());
+ if (!request) {
+ LOG(V4L2Compat, Error) << "Can't create request";
+ *ret = -ENOMEM;
+ return;
+ }
+
+ *ret = request->addBuffer(std::move(buffer));
+ if (*ret < 0) {
+ LOG(V4L2Compat, Error) << "Can't set buffer for request";
+ *ret = -ENOMEM;
+ return;
+ }
+
+ if (!isRunning_) {
+ pendingRequests_.push_back(std::move(request));
+ return;
+ }
+
+ *ret = camera_->queueRequest(request.release());
+ if (*ret < 0) {
+ LOG(V4L2Compat, Error) << "Can't queue request";
+ if (*ret == -EACCES)
+ *ret = -EBUSY;
+ }
+}