/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2019, Google Inc. * * v4l2_camera.cpp - V4L2 compatibility camera */ #include "v4l2_camera.h" #include #include "log.h" using namespace libcamera; LOG_DECLARE_CATEGORY(V4L2Compat); V4L2Camera::V4L2Camera(std::shared_ptr camera) : camera_(camera), isRunning_(false), bufferAllocator_(nullptr) { camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete); } V4L2Camera::~V4L2Camera() { close(); } int V4L2Camera::open() { /* \todo Support multiple open. */ if (camera_->acquire() < 0) { LOG(V4L2Compat, Error) << "Failed to acquire camera"; return -EINVAL; } config_ = camera_->generateConfiguration({ StreamRole::Viewfinder }); if (!config_) { camera_->release(); return -EINVAL; } bufferAllocator_ = FrameBufferAllocator::create(camera_); return 0; } void V4L2Camera::close() { delete bufferAllocator_; bufferAllocator_ = nullptr; camera_->release(); } void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig) { *streamConfig = config_->at(0); } std::vector V4L2Camera::completedBuffers() { std::vector v; bufferLock_.lock(); for (std::unique_ptr &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(); FrameBuffer *buffer = request->buffers().begin()->second; std::unique_ptr metadata = std::make_unique(request->cookie(), buffer->metadata()); completedBuffers_.push_back(std::move(metadata)); bufferLock_.unlock(); bufferSema_.release(); } int V4L2Camera::configure(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"; return -EINVAL; } if (validation == CameraConfiguration::Adjusted) LOG(V4L2Compat, Debug) << "Configuration adjusted"; LOG(V4L2Compat, Debug) << "Validated configuration is: " << streamConfig.toString(); int ret = camera_->configure(config_.get()); if (ret < 0) return ret; *streamConfigOut = config_->at(0); return 0; } int V4L2Camera::allocBuffers(unsigned int count) { Stream *stream = *camera_->streams().begin(); return bufferAllocator_->allocate(stream); } void V4L2Camera::freeBuffers() { Stream *stream = *camera_->streams().begin(); bufferAllocator_->free(stream); } FileDescriptor V4L2Camera::getBufferFd(unsigned int index) { Stream *stream = *camera_->streams().begin(); const std::vector> &buffers = bufferAllocator_->buffers(stream); if (buffers.size() <= index) return FileDescriptor(); return buffers[index]->planes()[0].fd; } int V4L2Camera::streamOn() { if (isRunning_) return 0; int ret = camera_->start(); if (ret < 0) return ret == -EACCES ? -EBUSY : ret; isRunning_ = true; for (std::unique_ptr &req : pendingRequests_) { /* \todo What should we do if this returns -EINVAL? */ ret = camera_->queueRequest(req.release()); if (ret < 0) return ret == -EACCES ? -EBUSY : ret; } pendingRequests_.clear(); return 0; } int V4L2Camera::streamOff() { /* \todo Restore buffers to reqbufs state? */ if (!isRunning_) return 0; int ret = camera_->stop(); if (ret < 0) return ret == -EACCES ? -EBUSY : ret; isRunning_ = false; return 0; } int V4L2Camera::qbuf(unsigned int index) { std::unique_ptr request = std::unique_ptr(camera_->createRequest(index)); if (!request) { LOG(V4L2Compat, Error) << "Can't create request"; return -ENOMEM; } Stream *stream = config_->at(0).stream(); FrameBuffer *buffer = bufferAllocator_->buffers(stream)[index].get(); int ret = request->addBuffer(stream, buffer); if (ret < 0) { LOG(V4L2Compat, Error) << "Can't set buffer for request"; return -ENOMEM; } if (!isRunning_) { pendingRequests_.push_back(std::move(request)); return 0; } ret = camera_->queueRequest(request.release()); if (ret < 0) { LOG(V4L2Compat, Error) << "Can't queue request"; return ret == -EACCES ? -EBUSY : ret; } return 0; }