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>
This commit is contained in:
parent
a3b80f3af8
commit
0ce8f2390b
10 changed files with 1486 additions and 0 deletions
224
src/v4l2/v4l2_camera.cpp
Normal file
224
src/v4l2/v4l2_camera.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue