From patchwork Wed Feb 6 06:07:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 520 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8429E61022 for ; Wed, 6 Feb 2019 07:08:24 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 32E2341 for ; Wed, 6 Feb 2019 07:08:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433304; bh=d8wcCrzGR1tGUHS6G4gxu7udEN+yJlkjmCc3PwBAbV0=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HPPi8oDeUiAxkO5qiOvmsRUrq6JaSu3rdtLjeRGtItXebxPrml3DEmPk+7Af2TXc6 CNh21yRA3k2yqXBPzj7dhWWcIxGWgDnWor9cNEZJmSvoYY9HX64ic0EmGZGO+3odHb hC78DQmxHQLckvPwNlrshwO3gaeqSZbHybEui9FI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:56 +0200 Message-Id: <20190206060818.13907-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190206060818.13907-1-laurent.pinchart@ideasonboard.com> References: <20190206060818.13907-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 05/27] libcamera: v4l2_device: Implement queue/dequeue operations X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 06 Feb 2019 06:08:24 -0000 From: Kieran Bingham Provide queueBuffer() and dequeueBuffer() methods to interact with the V4L2Device. Buffers will be directly referenced from the bufferPool of the V4L2Device based on the index in the pool. The V4L2Device is now opened in non-blocking mode in order to avoid blocking the dequeueBuffer() method. A signal is emitted when a buffer is ready and has been dequeued. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- src/libcamera/include/v4l2_device.h | 13 +++ src/libcamera/v4l2_device.cpp | 129 +++++++++++++++++++++++++++- 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h index 510b74f12d05..30a8f77d9772 100644 --- a/src/libcamera/include/v4l2_device.h +++ b/src/libcamera/include/v4l2_device.h @@ -7,15 +7,19 @@ #ifndef __LIBCAMERA_V4L2_DEVICE_H__ #define __LIBCAMERA_V4L2_DEVICE_H__ +#include #include #include #include +#include + namespace libcamera { class Buffer; class BufferPool; +class EventNotifier; class MediaEntity; struct V4L2Capability final : v4l2_capability { @@ -96,6 +100,9 @@ public: int exportBuffers(unsigned int count, BufferPool *pool); int releaseBuffers(); + int queueBuffer(Buffer *buffer); + Signal bufferReady; + private: int getFormatSingleplane(V4L2DeviceFormat *format); int setFormatSingleplane(V4L2DeviceFormat *format); @@ -107,6 +114,9 @@ private: int createPlane(Buffer *buffer, unsigned int plane, unsigned int length); + Buffer *dequeueBuffer(); + void bufferAvailable(EventNotifier *notifier); + std::string deviceNode_; int fd_; V4L2Capability caps_; @@ -115,6 +125,9 @@ private: enum v4l2_memory memoryType_; BufferPool *bufferPool_; + std::atomic queuedBuffersCount_; + + EventNotifier *fdEvent_; }; } /* namespace libcamera */ diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 2d0a1cb6abbe..134a468c4236 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "log.h" #include "media_object.h" @@ -203,6 +204,10 @@ LOG_DEFINE_CATEGORY(V4L2) * No API call other than open(), isOpen() and close() shall be called on an * unopened device instance. * + * The V4L2Device class tracks queued buffers and handles buffer events. It + * automatically dequeues completed buffers and emits the \ref bufferReady + * signal. + * * Upon destruction any device left open will be closed, and any resources * released. */ @@ -212,7 +217,8 @@ LOG_DEFINE_CATEGORY(V4L2) * \param deviceNode The file-system path to the video device node */ V4L2Device::V4L2Device(const std::string &deviceNode) - : deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr) + : deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr), + queuedBuffersCount_(0), fdEvent_(nullptr) { /* * We default to an MMAP based CAPTURE device, however this will be @@ -251,7 +257,7 @@ int V4L2Device::open() return -EBUSY; } - ret = ::open(deviceNode_.c_str(), O_RDWR); + ret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK); if (ret < 0) { ret = -errno; LOG(V4L2, Error) @@ -294,6 +300,10 @@ int V4L2Device::open() ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT; + fdEvent_ = new EventNotifier(fd_, EventNotifier::Read); + fdEvent_->activated.connect(this, &V4L2Device::bufferAvailable); + fdEvent_->setEnabled(false); + return 0; } @@ -315,6 +325,7 @@ void V4L2Device::close() return; releaseBuffers(); + delete fdEvent_; ::close(fd_); fd_ = -1; @@ -631,4 +642,118 @@ int V4L2Device::releaseBuffers() return 0; } +/** + * \brief Queue a buffer into the device + * \param[in] buffer The buffer to be queued + * + * For capture devices the \a buffer will be filled with data by the device. + * For output devices the \a buffer shall contain valid data and will be + * processed by the device. Once the device has finished processing the buffer, + * it will be available for dequeue. + * + * \todo Support output devices (bytesused, ...) + * \todo Support imported buffers (dmabuf fd) + * + * \return 0 on success or a negative error number otherwise + */ +int V4L2Device::queueBuffer(Buffer *buffer) +{ + struct v4l2_buffer buf = {}; + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; + int ret; + + buf.index = buffer->index(); + buf.type = bufferType_; + buf.memory = memoryType_; + + if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) { + buf.length = buffer->planes().size(); + buf.m.planes = planes; + } + + LOG(V4L2, Debug) << "Queueing buffer " << buf.index; + + ret = ioctl(fd_, VIDIOC_QBUF, &buf); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to queue buffer " << buf.index << ": " + << strerror(-ret); + return ret; + } + + if (queuedBuffersCount_++ == 0) + fdEvent_->setEnabled(true); + + return 0; +} + +/** + * \brief Dequeue the next available buffer from the device + * + * This method dequeues the next available buffer from the device. If no buffer + * is available to be dequeued it will return nullptr immediately. + * + * \return A pointer to the dequeued buffer on succcess, or nullptr otherwise + */ +Buffer *V4L2Device::dequeueBuffer() +{ + struct v4l2_buffer buf = {}; + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; + int ret; + + buf.type = bufferType_; + buf.memory = memoryType_; + + if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) { + buf.length = VIDEO_MAX_PLANES; + buf.m.planes = planes; + } + + ret = ioctl(fd_, VIDIOC_DQBUF, &buf); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to dequeue buffer: " << strerror(-ret); + return nullptr; + } + + ASSERT(buf.index < bufferPool_->count()); + + if (--queuedBuffersCount_ == 0) + fdEvent_->setEnabled(false); + + return &bufferPool_->buffers()[buf.index]; +} + +/** + * \brief Slot to handle completed buffer events from the V4L2 device + * \param[in] notifier The event notifier + * + * When this slot is called, a Buffer has become available from the device, and + * will be emitted through the bufferReady Signal. + * + * For Capture devices the Buffer will contain valid data. + * For Output devices the Buffer can be considered empty. + */ +void V4L2Device::bufferAvailable(EventNotifier *notifier) +{ + Buffer *buffer = dequeueBuffer(); + if (!buffer) + return; + + LOG(V4L2, Debug) << "Buffer " << buffer->index() << " is available"; + + /* Notify anyone listening to the device. */ + bufferReady.emit(buffer); + + /* Notify anyone listening to the buffer specifically. */ + buffer->completed.emit(buffer); +} + +/** + * \var V4L2Device::bufferReady + * \brief A Signal emitted when a buffer completes + */ + } /* namespace libcamera */