From patchwork Wed Feb 6 06:07:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 516 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 99B4361021 for ; Wed, 6 Feb 2019 07:08:23 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2EFAE2D7 for ; Wed, 6 Feb 2019 07:08:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433303; bh=FUtewdVKAYxafzetZ6UuBVTPQLD4D1IKuJRWky1K3io=; h=From:To:Subject:Date:In-Reply-To:References:From; b=CZePm75PxaNUUpuXwDudmqQ0yRDbJHNH+xYqA/5fNxp3framPJ+cTXtr+D0UaP1ny K0Ao4ZeQYDZD51XCIPSUqv3MSzDfoVaBvaR6xV1m1Fg6zCd+N/ERii1zwVsq8Vejrc mKSbPxG9atQQORV6wpdGBH/aoASHs0XUhxmhgfXA= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:52 +0200 Message-Id: <20190206060818.13907-2-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 01/27] libcamera: Add Buffer Management 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:23 -0000 From: Kieran Bingham Provide classes that represent frame buffers and pools of frame buffers. An image within the system may use one or more Plane objects to track each plane in the case of multi-planar image formats. The Buffer class manages all of the data required to render or interpret the raw image data. Signed-off-by: Laurent Pinchart Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Niklas Söderlund --- include/libcamera/buffer.h | 74 ++++++++++ include/libcamera/libcamera.h | 1 + include/libcamera/meson.build | 1 + src/libcamera/buffer.cpp | 253 ++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 5 files changed, 330 insertions(+) create mode 100644 include/libcamera/buffer.h create mode 100644 src/libcamera/buffer.cpp diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h new file mode 100644 index 000000000000..21a1ec4c574e --- /dev/null +++ b/include/libcamera/buffer.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * buffer.h - Buffer handling + */ +#ifndef __LIBCAMERA_BUFFER_H__ +#define __LIBCAMERA_BUFFER_H__ + +#include + +#include + +namespace libcamera { + +class BufferPool; + +class Plane final +{ +public: + Plane(); + ~Plane(); + + int dmabuf() const { return fd_; } + int setDmabuf(int fd, unsigned int length); + + void *mem(); + unsigned int length() const { return length_; } + +private: + int mmap(); + int munmap(); + + int fd_; + unsigned int length_; + void *mem_; +}; + +class Buffer final +{ +public: + Buffer(); + + unsigned int index() const { return index_; } + std::vector &planes() { return planes_; } + + Signal completed; + +private: + friend class BufferPool; + + unsigned int index_; + + std::vector planes_; +}; + +class BufferPool final +{ +public: + ~BufferPool(); + + void createBuffers(unsigned int count); + void destroyBuffers(); + + unsigned int count() const { return buffers_.size(); } + std::vector &buffers() { return buffers_; } + +private: + std::vector buffers_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_BUFFER_H__ */ diff --git a/include/libcamera/libcamera.h b/include/libcamera/libcamera.h index 272dfd5e4a67..8167e8099ac0 100644 --- a/include/libcamera/libcamera.h +++ b/include/libcamera/libcamera.h @@ -7,6 +7,7 @@ #ifndef __LIBCAMERA_LIBCAMERA_H__ #define __LIBCAMERA_LIBCAMERA_H__ +#include #include #include #include diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 54a680787e5c..8c14423bc444 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -1,4 +1,5 @@ libcamera_api = files([ + 'buffer.h', 'camera.h', 'camera_manager.h', 'event_dispatcher.h', diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp new file mode 100644 index 000000000000..5f6114cf3bc5 --- /dev/null +++ b/src/libcamera/buffer.cpp @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * buffer.cpp - Buffer handling + */ + +#include +#include +#include +#include + +#include + +#include "log.h" + +/** + * \file buffer.h + * \brief Buffer handling + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Buffer) + +/** + * \class Plane + * \brief A memory region to store a single plane of a frame + * + * Planar pixel formats use multiple memory regions to store planes + * corresponding to the different colour components of a frame. The Plane class + * tracks the specific details of a memory region used to store a single plane + * for a given frame and provides the means to access the memory, both for the + * application and for DMA. A Buffer then contains one or multiple planes + * depending on its pixel format. + * + * To support DMA access, planes are associated with dmabuf objects represented + * by file handles. Each plane carries a dmabuf file handle and an offset within + * the buffer. Those file handles may refer to the same dmabuf object, depending + * on whether the devices accessing the memory regions composing the image + * support non-contiguous DMA to planes ore require DMA-contiguous memory. + * + * To support CPU access, planes carry the CPU address of their backing memory. + * Similarly to the dmabuf file handles, the CPU addresses for planes composing + * an image may or may not be contiguous. + */ + +Plane::Plane() + : fd_(-1), length_(0), mem_(0) +{ +} + +Plane::~Plane() +{ + munmap(); + + if (fd_ != -1) + close(fd_); +} + +/** + * \fn Plane::dmabuf() + * \brief Get the dmabuf file handle backing the buffer + */ + +/** + * \brief Set the dmabuf file handle backing the buffer + * \param[in] fd The dmabuf file handle + * \param[in] length The size of the memory region + * + * The \a fd dmabuf file handle is duplicated and stored. The caller may close + * the original file handle. + * + * \return 0 on success or a negative error value otherwise. + */ +int Plane::setDmabuf(int fd, unsigned int length) +{ + if (fd < 0) { + LOG(Buffer, Error) << "Invalid dmabuf fd provided"; + return -EINVAL; + } + + if (fd_ != -1) { + close(fd_); + fd_ = -1; + } + + fd_ = dup(fd); + if (fd_ == -1) { + int ret = -errno; + LOG(Buffer, Error) + << "Failed to duplicate dmabuf: " << strerror(-ret); + return ret; + } + + length_ = length; + + return 0; +} + +/** + * \brief Map the plane memory data to a CPU accessible address + * + * The file descriptor to map the memory from must be set by a call to + * setDmaBuf() before calling this function. + * + * \sa setDmaBuf() + * + * \return 0 on success or a negative error value otherwise. + */ +int Plane::mmap() +{ + void *map; + + if (mem_) + return 0; + + map = ::mmap(NULL, length_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); + if (map == MAP_FAILED) { + int ret = -errno; + LOG(Buffer, Error) + << "Failed to mmap plane: " << strerror(-ret); + return ret; + } + + mem_ = map; + + return 0; +} + +/** + * \brief Unmap any existing CPU accessible mapping + * + * Unmap the memory mapped by an earlier call to mmap(). + * + * \return 0 on success or a negative error value otherwise. + */ +int Plane::munmap() +{ + int ret = 0; + + if (mem_) + ret = ::munmap(mem_, length_); + + if (ret) { + ret = -errno; + LOG(Buffer, Warning) + << "Failed to unmap plane: " << strerror(-ret); + } else { + mem_ = 0; + } + + return ret; +} + +/** + * \fn Plane::mem() + * \brief Retrieve the CPU accessible memory address of the Plane + * \return The CPU accessible memory address on success or nullptr otherwise. + */ +void *Plane::mem() +{ + if (!mem_) + mmap(); + + return mem_; +} + +/** + * \fn Plane::length() + * \brief Retrieve the length of the memory region + * \return The length of the memory region + */ + +/** + * \class Buffer + * \brief A memory buffer to store an image + * + * The Buffer class represents the memory buffers used to store a + * full frame image, which may contain multiple separate memory Plane + * objects if the image format is multi-planar. + */ + +Buffer::Buffer() + : index_(-1) +{ +} + +/** + * \fn Buffer::index() + * \brief Retrieve the Buffer index + * \return The buffer index + */ + +/** + * \fn Buffer::planes() + * \brief Retrieve the planes within the buffer + * \return A reference to a vector holding all Planes within the buffer + */ + +/** + * \var Buffer::completed + * \brief A Signal to provide notifications that the specific Buffer is ready + */ + +/** + * \class BufferPool + * \brief A pool of buffers + * + * The BufferPool class groups together a collection of Buffers to store frames. + * The buffers must be exported by a device before they can be imported into + * another device for further use. + */ + +BufferPool::~BufferPool() +{ + destroyBuffers(); +} + +/** + * \brief Create buffers in the Pool + * \param[in] count The number of buffers to create + */ +void BufferPool::createBuffers(unsigned int count) +{ + unsigned int index = 0; + + buffers_.resize(count); + for (Buffer &buffer : buffers_) + buffer.index_ = index++; +} + +/** + * \brief Release all buffers from pool + */ +void BufferPool::destroyBuffers() +{ + buffers_.resize(0); +} + +/** + * \fn BufferPool::count() + * \brief Retrieve the number of buffers contained within the pool + * \return The number of buffers contained in the pool + */ + +/** + * \fn BufferPool::buffers() + * \brief Retrieve all the buffers in the pool + * \return A vector containing all the buffers in the pool. + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 9f6ff99eebe2..a4e9cc8f936c 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -1,4 +1,5 @@ libcamera_sources = files([ + 'buffer.cpp', 'camera.cpp', 'camera_manager.cpp', 'device_enumerator.cpp', From patchwork Wed Feb 6 06:07:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 517 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CD71761022 for ; Wed, 6 Feb 2019 07:08:23 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6C11B2D8 for ; Wed, 6 Feb 2019 07:08:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433303; bh=cccDYJYCqgClqeKTmMAB9NH3IcXKA0AXfF7TghxU+UU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=USzbhqmKbdy2P1Hlvto41fEKb6B19J/e9fKHAD4JHFip8Yx7Xg9ueBMpnOptJYUyx nxOrFnGlfI25cQstjc1Q53MCMX7DfvJ7AgW/vq2a4DUb5oCV+BCqZD4Yxy2ccfuOAU CzeSSp2/RgaQag1owdZaJqA5PjB2vN7FZu4yY7R4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:53 +0200 Message-Id: <20190206060818.13907-3-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 02/27] test: v4l2_device: Use DeviceEnumerator to find a UVCVideo 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 Utilise the existing DeviceEnumerator system to identify a suitable V4L2Device for testing. Specifically target these tests at a uvcvideo driver based device as a known supported target. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- test/v4l2_device/v4l2_device_test.cpp | 34 +++++++++++++++++++++++---- test/v4l2_device/v4l2_device_test.h | 7 ++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/test/v4l2_device/v4l2_device_test.cpp b/test/v4l2_device/v4l2_device_test.cpp index 362553712caa..18d014caf4c8 100644 --- a/test/v4l2_device/v4l2_device_test.cpp +++ b/test/v4l2_device/v4l2_device_test.cpp @@ -10,6 +10,10 @@ #include "v4l2_device_test.h" +#include "device_enumerator.h" +#include "media_device.h" + +using namespace std; using namespace libcamera; bool exists(const std::string &path) @@ -24,20 +28,40 @@ bool exists(const std::string &path) int V4L2DeviceTest::init() { - const std::string device("/dev/video0"); + enumerator_ = DeviceEnumerator::create(); + if (!enumerator_) { + cerr << "Failed to create device enumerator" << endl; + return TestFail; + } + + if (enumerator_->enumerate()) { + cerr << "Failed to enumerate media devices" << endl; + return TestFail; + } - /* Validate the device node exists. */ - if (!exists(device)) { - std::cout << "No video device available" << std::endl; + DeviceMatch dm("uvcvideo"); + media_ = std::move(enumerator_->search(dm)); + if (!media_) return TestSkip; + + media_->acquire(); + + for (MediaEntity *entity : media_->entities()) { + if (entity->flags() & MEDIA_ENT_FL_DEFAULT) { + dev_ = new V4L2Device(entity); + break; + } } - dev_ = new V4L2Device(device); + if (!dev_) + return TestSkip; return dev_->open(); } void V4L2DeviceTest::cleanup() { + media_->release(); + delete dev_; }; diff --git a/test/v4l2_device/v4l2_device_test.h b/test/v4l2_device/v4l2_device_test.h index 405cb7d6f404..ca231ab47fde 100644 --- a/test/v4l2_device/v4l2_device_test.h +++ b/test/v4l2_device/v4l2_device_test.h @@ -7,7 +7,12 @@ #ifndef __LIBCAMERA_V4L2_DEVICE_TEST_H_ #define __LIBCAMERA_V4L2_DEVICE_TEST_H_ +#include + #include "test.h" + +#include "device_enumerator.h" +#include "media_device.h" #include "v4l2_device.h" using namespace libcamera; @@ -21,6 +26,8 @@ protected: int init(); void cleanup(); + std::unique_ptr enumerator_; + std::shared_ptr media_; V4L2Device *dev_; }; From patchwork Wed Feb 6 06:07:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 518 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 12EE361022 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 AE58141 for ; Wed, 6 Feb 2019 07:08:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433303; bh=wm9SKSfXHld43zjn4G73Pb8itDkjVwXqFj0dqKP0qL0=; h=From:To:Subject:Date:In-Reply-To:References:From; b=kq+hJ6WK2BmEBQBdreUYmgVrFCCCtsXj0LFwwSP5qQNCiqN0JCQ4mUYFjV6PMninb fuhyvx9p1diBhcVS13xdBiJyx1YID+LPLuAJVPOWiCkJwM776p/HtpsERqDsgpCEPG 5SpeMbuLurHxZroVSOUYyxh2/GyndrRC7eQkvLXw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:54 +0200 Message-Id: <20190206060818.13907-4-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 03/27] libcamera: v4l2_device: Request buffers from the device 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 an exportBuffers() function which allocates buffers with the MMAP method, exports them using the dmabuf API and populates the given BufferPool. 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 | 17 ++- src/libcamera/v4l2_device.cpp | 158 +++++++++++++++++++++++++++- 2 files changed, 173 insertions(+), 2 deletions(-) diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h index 87cde10d2417..510b74f12d05 100644 --- a/src/libcamera/include/v4l2_device.h +++ b/src/libcamera/include/v4l2_device.h @@ -8,11 +8,16 @@ #define __LIBCAMERA_V4L2_DEVICE_H__ #include +#include #include namespace libcamera { +class Buffer; +class BufferPool; +class MediaEntity; + struct V4L2Capability final : v4l2_capability { const char *driver() const { @@ -67,7 +72,6 @@ public: unsigned int planesCount; }; -class MediaEntity; class V4L2Device { public: @@ -89,6 +93,9 @@ public: int getFormat(V4L2DeviceFormat *format); int setFormat(V4L2DeviceFormat *format); + int exportBuffers(unsigned int count, BufferPool *pool); + int releaseBuffers(); + private: int getFormatSingleplane(V4L2DeviceFormat *format); int setFormatSingleplane(V4L2DeviceFormat *format); @@ -96,10 +103,18 @@ private: int getFormatMultiplane(V4L2DeviceFormat *format); int setFormatMultiplane(V4L2DeviceFormat *format); + int requestBuffers(unsigned int count); + int createPlane(Buffer *buffer, unsigned int plane, + unsigned int length); + std::string deviceNode_; int fd_; V4L2Capability caps_; + enum v4l2_buf_type bufferType_; + enum v4l2_memory memoryType_; + + BufferPool *bufferPool_; }; } /* namespace libcamera */ diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 1823457529f5..2d0a1cb6abbe 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -10,6 +10,9 @@ #include #include #include +#include + +#include #include "log.h" #include "media_object.h" @@ -209,8 +212,14 @@ 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) + : deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr) { + /* + * We default to an MMAP based CAPTURE device, however this will be + * updated based upon the device capabilities. + */ + bufferType_ = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + memoryType_ = V4L2_MEMORY_MMAP; } /** @@ -305,6 +314,8 @@ void V4L2Device::close() if (fd_ < 0) return; + releaseBuffers(); + ::close(fd_); fd_ = -1; } @@ -475,4 +486,149 @@ int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format) return 0; } +int V4L2Device::requestBuffers(unsigned int count) +{ + struct v4l2_requestbuffers rb = {}; + int ret; + + rb.count = count; + rb.type = bufferType_; + rb.memory = memoryType_; + + ret = ioctl(fd_, VIDIOC_REQBUFS, &rb); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Unable to request " << count << " buffers: " + << strerror(-ret); + return ret; + } + + LOG(V4L2, Debug) + << deviceNode_ << ":" << rb.count << " buffers requested."; + + return rb.count; +} + +/** + * \brief Request \a count buffers to be allocated from the device and stored in + * the buffer pool provided. + * \param[in] count Number of buffers to allocate + * \param[out] pool BufferPool to populate with buffers + * \return 0 on success or a negative error code otherwise + */ +int V4L2Device::exportBuffers(unsigned int count, BufferPool *pool) +{ + unsigned int allocatedBuffers; + unsigned int i; + int ret; + + memoryType_ = V4L2_MEMORY_MMAP; + + ret = requestBuffers(count); + if (ret < 0) + return ret; + + allocatedBuffers = ret; + if (allocatedBuffers < count) { + LOG(V4L2, Error) << "Not enough buffers provided by V4L2Device"; + requestBuffers(0); + return -ENOMEM; + } + + count = ret; + + /* Map the buffers. */ + for (i = 0; i < count; ++i) { + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; + struct v4l2_buffer buf = {}; + struct Buffer &buffer = pool->buffers()[i]; + + buf.index = i; + buf.type = bufferType_; + buf.memory = memoryType_; + buf.length = VIDEO_MAX_PLANES; + buf.m.planes = planes; + + ret = ioctl(fd_, VIDIOC_QUERYBUF, &buf); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Unable to query buffer " << i << ": " + << strerror(-ret); + break; + } + + if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) { + for (unsigned int p = 0; p < buf.length; ++p) { + ret = createPlane(&buffer, p, + buf.m.planes[p].length); + if (ret) + break; + } + } else { + ret = createPlane(&buffer, 0, buf.length); + } + + if (ret) { + LOG(V4L2, Error) << "Failed to create plane"; + break; + } + } + + if (ret) { + requestBuffers(0); + pool->destroyBuffers(); + return ret; + } + + bufferPool_ = pool; + + return 0; +} + +int V4L2Device::createPlane(Buffer *buffer, unsigned int planeIndex, + unsigned int length) +{ + struct v4l2_exportbuffer expbuf = {}; + int ret; + + LOG(V4L2, Debug) + << "Buffer " << buffer->index() + << " plane " << planeIndex + << ": length=" << length; + + expbuf.type = bufferType_; + expbuf.index = buffer->index(); + expbuf.plane = planeIndex; + expbuf.flags = O_RDWR; + + ret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to export buffer: " << strerror(-ret); + return ret; + } + + buffer->planes().emplace_back(); + Plane &plane = buffer->planes().back(); + plane.setDmabuf(expbuf.fd, length); + + return 0; +} + +/** + * \brief Release all internally allocated buffers + */ +int V4L2Device::releaseBuffers() +{ + LOG(V4L2, Debug) << "Releasing bufferPool"; + + requestBuffers(0); + bufferPool_ = nullptr; + + return 0; +} + } /* namespace libcamera */ From patchwork Wed Feb 6 06:07:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 519 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 5013661022 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 EC8522D7 for ; Wed, 6 Feb 2019 07:08:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433304; bh=Pu+ypCssb0+WQtotRHwEBMjRh/ieRz+Jz2FNnrDcU1M=; h=From:To:Subject:Date:In-Reply-To:References:From; b=h6jdEsn9fSCJ+DSOWNznU866qyHnsfoeFRNJ3HjB+NeE8t8/5ucJ/YBkt4zqLEhSA fQl+6iQttlK3SKOKbvAVyZhsq04wqBdps6lAIQ3n/e8DaufLkjVoGHEuQZE+WsTMSM 1VmRnrA6s0mLNPJr0MR9mXqJio2G8pncJMqTNSRQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:55 +0200 Message-Id: <20190206060818.13907-5-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 04/27] test: v4l2_device: Add request_buffers test 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 Add a utility to the test suite to request and allocate buffers from a V4L2Device to ensure it functions correctly. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- test/v4l2_device/meson.build | 1 + test/v4l2_device/request_buffers.cpp | 32 ++++++++++++++++++++++++++++ test/v4l2_device/v4l2_device_test.h | 7 +++++- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 test/v4l2_device/request_buffers.cpp diff --git a/test/v4l2_device/meson.build b/test/v4l2_device/meson.build index 41675a303498..b6b672611d60 100644 --- a/test/v4l2_device/meson.build +++ b/test/v4l2_device/meson.build @@ -2,6 +2,7 @@ # They are not alphabetically sorted. v4l2_device_tests = [ [ 'double_open', 'double_open.cpp' ], + [ 'request_buffers', 'request_buffers.cpp' ], ] foreach t : v4l2_device_tests diff --git a/test/v4l2_device/request_buffers.cpp b/test/v4l2_device/request_buffers.cpp new file mode 100644 index 000000000000..bc6ff2c18a57 --- /dev/null +++ b/test/v4l2_device/request_buffers.cpp @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * libcamera V4L2 API tests + */ + +#include "v4l2_device_test.h" + +class RequestBuffersTest : public V4L2DeviceTest +{ +protected: + int run() + { + /* + * TODO: + * Test invalid requests + * Test different buffer allocations + */ + const unsigned int bufferCount = 8; + + createBuffers(bufferCount); + + int ret = dev_->exportBuffers(bufferCount, &pool_); + if (ret) + return TestFail; + + return TestPass; + } +}; + +TEST_REGISTER(RequestBuffersTest); diff --git a/test/v4l2_device/v4l2_device_test.h b/test/v4l2_device/v4l2_device_test.h index ca231ab47fde..f22f0bb555d8 100644 --- a/test/v4l2_device/v4l2_device_test.h +++ b/test/v4l2_device/v4l2_device_test.h @@ -9,6 +9,8 @@ #include +#include + #include "test.h" #include "device_enumerator.h" @@ -20,7 +22,9 @@ using namespace libcamera; class V4L2DeviceTest : public Test { public: - V4L2DeviceTest() : dev_(nullptr) { }; + V4L2DeviceTest() : dev_(nullptr){}; + + void createBuffers(unsigned int qty) { pool_.createBuffers(qty); } protected: int init(); @@ -29,6 +33,7 @@ protected: std::unique_ptr enumerator_; std::shared_ptr media_; V4L2Device *dev_; + BufferPool pool_; }; #endif /* __LIBCAMERA_V4L2_DEVICE_TEST_H_ */ 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 */ From patchwork Wed Feb 6 06:07:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 521 X-Patchwork-Delegate: laurent.pinchart@ideasonboard.com 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 C1BAE61022 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 6B0CD2D7 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=vdNnO7uzLhJIZcdswDjBiG1uwUAte5M9V//IYPhHTIE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=pZP3cOFBe/t/yCDmCbVnDHKaiGPYq7ZKay43xqeCy821XYuomxXLIx4Z+FanZXhHy wdPiesJStn5OL3fABgZfP+z366OR39aQEOV8BY58ICvZHbgD2dzvzv5t/qYDHhWrVs HqgZdQSGLrZSK8V4+FDyP8dV+E7hHx7j9NbVXrfg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:57 +0200 Message-Id: <20190206060818.13907-7-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 06/27] libcamera: v4l2_device: Implement stream{On, Off} 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:25 -0000 From: Kieran Bingham Support starting and stopping a stream on a V4L2 device. Buffers must be queued before the stream is started. 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 | 3 ++ src/libcamera/v4l2_device.cpp | 45 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h index 30a8f77d9772..988e646c5de1 100644 --- a/src/libcamera/include/v4l2_device.h +++ b/src/libcamera/include/v4l2_device.h @@ -103,6 +103,9 @@ public: int queueBuffer(Buffer *buffer); Signal bufferReady; + int streamOn(); + int streamOff(); + private: int getFormatSingleplane(V4L2DeviceFormat *format); int setFormatSingleplane(V4L2DeviceFormat *format); diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 134a468c4236..f9839fc715f4 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -756,4 +756,49 @@ void V4L2Device::bufferAvailable(EventNotifier *notifier) * \brief A Signal emitted when a buffer completes */ +/** + * \brief Start the video stream + * + * \return 0 on success or a negative error code otherwise + */ +int V4L2Device::streamOn() +{ + int ret; + + ret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to start streaming: " << strerror(-ret); + return ret; + } + + return 0; +} + +/** + * \brief Stop the video stream + * + * \todo Ensure completion notifications are sent for all queued buffers + * + * \return 0 on success or a negative error code otherwise + */ +int V4L2Device::streamOff() +{ + int ret; + + ret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to stop streaming: " << strerror(-ret); + return ret; + } + + queuedBuffersCount_ = 0; + fdEvent_->setEnabled(false); + + return 0; +} + } /* namespace libcamera */ From patchwork Wed Feb 6 06:07:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 522 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 093FD61022 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A6A0041 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=OdxFgULqG4rAk37WjRsRqA7aiL0f50Ylmft6C1HX01Y=; h=From:To:Subject:Date:In-Reply-To:References:From; b=j8N+JwroefUHT9IS8uODtKyFAxEoSZ+SjMAb66Qcn6g3TYx2eUkZ9gaoR5bUL6FOu 78DTakPmz7XDJ+4AzYZwmUHnTxRH8IDbKImYBXZYx+RI1lmcU0xx9tnzN5espkfnln aEJf0ablxIbML0RUPxiRct+4Gwhf2tSNTG/Ym8mw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:58 +0200 Message-Id: <20190206060818.13907-8-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 07/27] test: v4l2_device: Add StreamOn/StreamOff test 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:26 -0000 From: Kieran Bingham Provide a small test to exercise the streamOn() and streamOff() calls. 8 buffers are requested locally. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- test/v4l2_device/meson.build | 1 + test/v4l2_device/stream_on_off.cpp | 35 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 test/v4l2_device/stream_on_off.cpp diff --git a/test/v4l2_device/meson.build b/test/v4l2_device/meson.build index b6b672611d60..cbaa79da9b81 100644 --- a/test/v4l2_device/meson.build +++ b/test/v4l2_device/meson.build @@ -3,6 +3,7 @@ v4l2_device_tests = [ [ 'double_open', 'double_open.cpp' ], [ 'request_buffers', 'request_buffers.cpp' ], + [ 'stream_on_off', 'stream_on_off.cpp' ], ] foreach t : v4l2_device_tests diff --git a/test/v4l2_device/stream_on_off.cpp b/test/v4l2_device/stream_on_off.cpp new file mode 100644 index 000000000000..b564d2a2ab67 --- /dev/null +++ b/test/v4l2_device/stream_on_off.cpp @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * libcamera V4L2 API tests + */ + +#include "v4l2_device_test.h" + +class StreamOnStreamOffTest : public V4L2DeviceTest +{ +protected: + int run() + { + const unsigned int bufferCount = 8; + + createBuffers(bufferCount); + + int ret = dev_->exportBuffers(bufferCount, &pool_); + if (ret) + return TestFail; + + ret = dev_->streamOn(); + if (ret) + return TestFail; + + ret = dev_->streamOff(); + if (ret) + return TestFail; + + return TestPass; + } +}; + +TEST_REGISTER(StreamOnStreamOffTest); From patchwork Wed Feb 6 06:07:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 523 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 40EFB61031 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E1B472D7 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=1549433305; bh=YIvPvFriiZwE+IFaKBq5lVADB1qrbFQm8NRuhv1MsSo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=vA/z/mLdcvK/MDSRjGe2Pr0H/J8WLM52g56kNauK8rQzF287C7m8kgih7j6FJHsFD vVHL49cEMDHmxvA9A7CgmzA+bbck1neJI4A71xRHkk5LSCo66OP+Mw9XCdtvz5qIjl 0FBsyJeUfZT4q6vaRmJV5iAtsSf13KvYSNiMVxFQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:59 +0200 Message-Id: <20190206060818.13907-9-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 08/27] test: v4l2_device: Provide asynchronous capture test 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:26 -0000 From: Kieran Bingham Utilise the event_dispatcher to create a default event loop, and process asynchronous buffer receive events. If no frames are captured in 5 seconds, the test will fail. It will also fail if less than 30 frames have been captured in the same timeout period. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- test/v4l2_device/capture_async.cpp | 86 ++++++++++++++++++++++++++++++ test/v4l2_device/meson.build | 1 + 2 files changed, 87 insertions(+) create mode 100644 test/v4l2_device/capture_async.cpp diff --git a/test/v4l2_device/capture_async.cpp b/test/v4l2_device/capture_async.cpp new file mode 100644 index 000000000000..7a0735f65535 --- /dev/null +++ b/test/v4l2_device/capture_async.cpp @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * libcamera V4L2 API tests + */ + +#include +#include +#include +#include + +#include + +#include "v4l2_device_test.h" + +class CaptureAsyncTest : public V4L2DeviceTest +{ +public: + CaptureAsyncTest() + : frames(0){}; + + void receiveBuffer(Buffer *buffer) + { + std::cout << "Received buffer " << buffer->index() << std::endl; + frames++; + + /* Requeue the buffer for further use. */ + dev_->queueBuffer(buffer); + } + +protected: + int run() + { + const unsigned int bufferCount = 8; + + EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); + Timer timeout; + int ret; + + createBuffers(bufferCount); + + ret = dev_->exportBuffers(bufferCount, &pool_); + if (ret) + return TestFail; + + dev_->bufferReady.connect(this, &CaptureAsyncTest::receiveBuffer); + + /* Queue all the buffers to the device. */ + for (Buffer &b : pool_.buffers()) { + if (dev_->queueBuffer(&b)) + return TestFail; + } + + ret = dev_->streamOn(); + if (ret) + return TestFail; + + timeout.start(5000); + while (timeout.isRunning()) + dispatcher->processEvents(); + + if (frames < 1) { + std::cout << "Failed to capture any frames within timeout." << std::endl; + return TestFail; + } + + if (frames < 30) { + std::cout << "Failed to capture 30 frames within timeout." << std::endl; + return TestFail; + } + + std::cout << "Processed " << frames << " frames" << std::endl; + + ret = dev_->streamOff(); + if (ret) + return TestFail; + + return TestPass; + } + +private: + unsigned int frames; +}; + +TEST_REGISTER(CaptureAsyncTest); diff --git a/test/v4l2_device/meson.build b/test/v4l2_device/meson.build index cbaa79da9b81..ec2c7f9f11ff 100644 --- a/test/v4l2_device/meson.build +++ b/test/v4l2_device/meson.build @@ -4,6 +4,7 @@ v4l2_device_tests = [ [ 'double_open', 'double_open.cpp' ], [ 'request_buffers', 'request_buffers.cpp' ], [ 'stream_on_off', 'stream_on_off.cpp' ], + [ 'capture_async', 'capture_async.cpp' ], ] foreach t : v4l2_device_tests From patchwork Wed Feb 6 06:08:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 524 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8034661023 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2633841 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433305; bh=B5M/twrOBtrd2Nb3EXgK8rTXGGs1GTb6g6u429P8jIg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=MIWAPcrFAoAbkz5uiUIeXkfmAlZhOJ1zgAJLmIhGlBAO6HOJAlIaXiKsnkZobH6Aw btCtK13tN9it+jxtXXy2NFh+PZEuMKyNNU90m08DguT3GwY6c1dWTa4IIp1A4C0Md7 ityqAH7Pwo7/2wI0bKZ1dOHYvecExoaNmkMNt3/c= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:00 +0200 Message-Id: <20190206060818.13907-10-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 09/27] libcamera: v4l2_device: Update dequeued buffer information 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:26 -0000 From: Niklas Söderlund Copy the information from the struct v4l2_buffer when dequeueing the buffer as applications need this information to make sense of the captured data. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- include/libcamera/buffer.h | 8 ++++++++ src/libcamera/buffer.cpp | 26 ++++++++++++++++++++++++++ src/libcamera/v4l2_device.cpp | 10 +++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index 21a1ec4c574e..dc9aaad12a81 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -7,6 +7,7 @@ #ifndef __LIBCAMERA_BUFFER_H__ #define __LIBCAMERA_BUFFER_H__ +#include #include #include @@ -42,14 +43,21 @@ public: Buffer(); unsigned int index() const { return index_; } + unsigned int bytesused() const { return bytesused_; } + uint64_t timestamp() const { return timestamp_; } + unsigned int sequence() const { return sequence_; } std::vector &planes() { return planes_; } Signal completed; private: friend class BufferPool; + friend class V4L2Device; unsigned int index_; + unsigned int bytesused_; + uint64_t timestamp_; + unsigned int sequence_; std::vector planes_; }; diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index 5f6114cf3bc5..9ec372c2981b 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -203,6 +203,32 @@ Buffer::Buffer() * \brief A Signal to provide notifications that the specific Buffer is ready */ +/** + * \fn Buffer::bytesused() + * \brief Retrieve the number of bytes occupied by the data in the buffer + * \return Number of bytes occupied in the buffer + */ + +/** + * \fn Buffer::timestamp() + * \brief Retrieve the time when the buffer was processed + * + * The timestamp is expressed as a number number of nanoseconds since the epoch. + * + * \return Timestamp when the buffer was processed + */ + +/** + * \fn Buffer::sequence() + * \brief Retrieve the buffer sequence number + * + * The sequence number is a monotonically increasing number assigned to the + * buffer processed by the stream. Gaps in the sequence numbers indicate + * dropped frames. + * + * \return Sequence number of the buffer + */ + /** * \class BufferPool * \brief A pool of buffers diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index f9839fc715f4..e8755be1f63f 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -723,7 +724,14 @@ Buffer *V4L2Device::dequeueBuffer() if (--queuedBuffersCount_ == 0) fdEvent_->setEnabled(false); - return &bufferPool_->buffers()[buf.index]; + Buffer *buffer = &bufferPool_->buffers()[buf.index]; + + buffer->bytesused_ = buf.bytesused; + buffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL + + buf.timestamp.tv_usec * 1000ULL; + buffer->sequence_ = buf.sequence; + + return buffer; } /** From patchwork Wed Feb 6 06:08:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 525 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B708A61020 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6065A2D8 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433305; bh=ed7zAfnTyKzgGDl6z7BGBCASUoPoQdL+D+VYDrRhisM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=FnYWRaoE4hQt4oOWkrW4PRyq1W3TqUVBrz10DJkqKO8xgtOduSaXWLevrL2eMIMWQ NW7lOZMICxRlyCihvM5JUywlwHnSZlDKp7aUccMp9FQeNpqFNvQUy0G82XPCerDrLD bIRkQsVpmkucdp9GGMMU93/VyQV3RfBWVmznHXn0= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:01 +0200 Message-Id: <20190206060818.13907-11-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 10/27] libcamera: v4l2_device: Add comments to method parameters 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:27 -0000 From: Jacopo Mondi Add missing parameter documentation for set and get format methods. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/libcamera/v4l2_device.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index e8755be1f63f..64325ff9f5d9 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -352,6 +352,8 @@ void V4L2Device::close() /** * \brief Retrieve the image format set on the V4L2 device + * \param[out] format The image format applied on the device + * * \return 0 for success, a negative error code otherwise */ int V4L2Device::getFormat(V4L2DeviceFormat *format) @@ -362,6 +364,7 @@ int V4L2Device::getFormat(V4L2DeviceFormat *format) /** * \brief Configure an image format on the V4L2 device + * \param[in] format The image format to apply to the device * * Apply the supplied \a format to the device, and return the actually * applied format parameters, as \ref V4L2Device::getFormat would do. From patchwork Wed Feb 6 06:08:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 526 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id EFD926101F for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9C4F341 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433305; bh=Ecz3XcRZGXvyn6AFi52ZSkpjWh6Zs/6I0DMJ3zuS4lg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=GCsql1FE2yay0tNY8aKXuOqqfj6YNkXWEESPwn4LnbOPzKQZASqA3AmhNebAwtxMR EhExYr/iSISvrNR0wV0Is/1kmbHAvsvdVzxz+0jKnq+RjVa6wrqap5ZS+DA53faYvN +UZ6bKygoBi4ug1rI/5WcspWPeuiSY3run2riyyY= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:02 +0200 Message-Id: <20190206060818.13907-12-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 11/27] libcamera: Add geometry.h 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:27 -0000 From: Jacopo Mondi Add geometry-related definitions in the geometry.h internal header. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/libcamera/geometry.cpp | 49 ++++++++++++++++++++++++++++++++ src/libcamera/include/geometry.h | 22 ++++++++++++++ src/libcamera/meson.build | 1 + 3 files changed, 72 insertions(+) create mode 100644 src/libcamera/geometry.cpp create mode 100644 src/libcamera/include/geometry.h diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp new file mode 100644 index 000000000000..57f4fc7716d9 --- /dev/null +++ b/src/libcamera/geometry.cpp @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * geometry.cpp - Geometry-related structures + */ + +#include "geometry.h" + +/** + * \file geometry.h + * \brief Data structures related to geometric objects + */ + +namespace libcamera { + +/** + * \struct Rectangle + * \brief Describe a rectangle's position and dimensions + * + * Rectangles are used to identify an area of an image. They are specified by + * the coordinates of top-left corner and their horizontal and vertical size. + * + * The measure unit of the rectangle coordinates and size, as well as the + * reference point from which the Rectangle::x and Rectangle::y displacements + * refers to, are defined by the context were rectangle is used. + */ + +/** + * \var Rectangle::x + * \brief The horizontal coordinate of the rectangle's top-left corner + */ + +/** + * \var Rectangle::y + * \brief The vertical coordinate of the rectangle's top-left corner + */ + +/** + * \var Rectangle::w + * \brief The distance between the left and right sides + */ + +/** + * \var Rectangle::h + * \brief The distance between the top and bottom sides + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/include/geometry.h b/src/libcamera/include/geometry.h new file mode 100644 index 000000000000..cc146da7cb0d --- /dev/null +++ b/src/libcamera/include/geometry.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * geometry.h - Geometry-related structure + */ + +#ifndef __LIBCAMERA_GEOMETRY_H__ +#define __LIBCAMERA_GEOMETRY_H__ + +namespace libcamera { + +struct Rectangle { + int x; + int y; + unsigned int w; + unsigned int h; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_GEOMETRY_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index a4e9cc8f936c..8b33c4b25c30 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -6,6 +6,7 @@ libcamera_sources = files([ 'event_dispatcher.cpp', 'event_dispatcher_poll.cpp', 'event_notifier.cpp', + 'geometry.cpp', 'log.cpp', 'media_device.cpp', 'media_object.cpp', From patchwork Wed Feb 6 06:08:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 527 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 380F761026 for ; Wed, 6 Feb 2019 07:08:26 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D278E2D8 for ; Wed, 6 Feb 2019 07:08:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433305; bh=O3mcBuKcrm1QhL6KMm2kJOMjJjiCP6DKOSN+AZnzdIs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=c4mBXQoVCjcVM1UynQzI+UhqlSo41zuj45Dx/WMtqADAeJmrBUP1qzlwl9TfnaEVi 5rz/N1HLbO0B/YI8tSaTnZnnnOkpFSqQuAi309APUBQp+mskKMEu+mfQDWixbYlJDb PcsRCvBLJFoJ91Tjm1U0Sd0EP331byM6Ysz3f5ng= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:03 +0200 Message-Id: <20190206060818.13907-13-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 12/27] libcamera: Add V4L2Subdevice 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:28 -0000 From: Jacopo Mondi Add V4L2Subdevice class that provides an interface to interact with V4L2 defined sub-devices. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/libcamera/include/v4l2_subdevice.h | 51 +++++ src/libcamera/meson.build | 1 + src/libcamera/v4l2_subdevice.cpp | 265 +++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 src/libcamera/include/v4l2_subdevice.h create mode 100644 src/libcamera/v4l2_subdevice.cpp diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h new file mode 100644 index 000000000000..8fd666078985 --- /dev/null +++ b/src/libcamera/include/v4l2_subdevice.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_subdevice.h - V4L2 Subdevice + */ +#ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__ +#define __LIBCAMERA_V4L2_SUBDEVICE_H__ + +#include + +namespace libcamera { + +struct Rectangle; + +struct V4L2SubdeviceFormat { + uint32_t mbus_code; + uint32_t width; + uint32_t height; +}; + +class V4L2Subdevice +{ +public: + explicit V4L2Subdevice(const MediaEntity *entity); + V4L2Subdevice(const V4L2Subdevice &) = delete; + V4L2Subdevice &operator=(const V4L2Subdevice &) = delete; + + int open(); + bool isOpen() const; + void close(); + + std::string deviceNode() const { return deviceNode_; } + + int setCrop(unsigned int pad, Rectangle *rect); + int setCompose(unsigned int pad, Rectangle *rect); + + int getFormat(unsigned int pad, V4L2SubdeviceFormat *format); + int setFormat(unsigned int pad, V4L2SubdeviceFormat *format); + +private: + int setSelection(unsigned int pad, unsigned int target, + Rectangle *rect); + + std::string deviceNode_; + int fd_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_V4L2_SUBDEVICE_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 8b33c4b25c30..ac991dc59d18 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -15,6 +15,7 @@ libcamera_sources = files([ 'stream.cpp', 'timer.cpp', 'v4l2_device.cpp', + 'v4l2_subdevice.cpp', ]) libcamera_headers = files([ diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp new file mode 100644 index 000000000000..1d5b11115dbb --- /dev/null +++ b/src/libcamera/v4l2_subdevice.cpp @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_subdevice.cpp - V4L2 Subdevice + */ + +#include +#include +#include +#include + +#include + +#include "geometry.h" +#include "log.h" +#include "media_object.h" +#include "v4l2_subdevice.h" + +/** + * \file v4l2_subdevice.h + * \brief V4L2 Subdevice API + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(V4L2Subdev) + +/** + * \struct V4L2SubdeviceFormat + * \brief The V4L2 sub-device image format and sizes + * + * This structure describes the format of images when transported between + * separate components connected through a physical bus, such as image sensor + * and image receiver or between components part of the same System-on-Chip that + * realize an image transformation pipeline. + * + * The format of images when transported on physical interconnections is known + * as the "media bus format", and it is identified by a resolution and a pixel + * format identification code, known as the "media bus code", not to be confused + * with the fourcc code that identify the format of images when stored in memory + * (see V4L2Device::V4L2DeviceFormat). + * + * Media Bus formats supported by the V4L2 APIs are described in Section + * 4.15.3.4.1 of the "Part I - Video for Linux API" chapter of the "Linux Media + * Infrastructure userspace API", part of the Linux kernel documentation. + * + * Image media bus formats are properties of the subdev pads. When images are + * transported between two media pads identified by a 0-indexed number, the + * image bus format configured on the two pads should match (according to the + * underlying driver format matching criteria) in order to prepare for a + * successful streaming operation. For a more detailed description of the image + * format negotiation process when performed between V4L2 subdevices, refer to + * Section 4.15.3.1 of the above mentioned Linux kernel documentation section. + */ + +/** + * \var V4L2SubdeviceFormat::width + * \brief The image width in pixels + */ + +/** + * \var V4L2SubdeviceFormat::height + * \brief The image height in pixels + */ + +/** + * \var V4L2SubdeviceFormat::mbus_code + * \brief The image format bus code + */ + +/** + * \class V4L2Subdevice + * \brief A V4L2 subdevice as exposed by the Linux kernel + * + * The V4L2Subdevice class provides an API to the "Sub-device interface" as + * described in section 4.15 of the "Linux Media Infrastructure userspace API" + * chapter of the Linux Kernel documentation. + * + * A V4L2Subdevice is constructed from a MediaEntity instance, using the system + * path of the entity's device node. No API call other than open(), isOpen() + * and close() shall be called on an unopened device instance. Upon destruction + * any device left open will be closed, and any resources released. + */ + +/** + * \brief Create a V4L2 subdevice from a MediaEntity using its device node + * path + */ +V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity) + : deviceNode_(entity->deviceNode()), fd_(-1) +{ +} + +/** + * \brief Open a V4L2 subdevice + * + * \return 0 on success, a negative error code otherwise + */ +int V4L2Subdevice::open() +{ + int ret; + + if (isOpen()) { + LOG(V4L2Subdev, Error) << "Device already open"; + return -EBUSY; + } + + ret = ::open(deviceNode_.c_str(), O_RDWR); + if (ret < 0) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Failed to open V4L2 subdevice '" << deviceNode_ + << "': " << strerror(-ret); + return ret; + } + fd_ = ret; + + return 0; +} + +/** + * \brief Check if the subdevice is open + * \return True if the subdevice is open, false otherwise + */ +bool V4L2Subdevice::isOpen() const +{ + return fd_ != -1; +} + +/** + * \brief Close the subdevice, releasing any resources acquired by open() + */ +void V4L2Subdevice::close() +{ + if (!isOpen()) + return; + + ::close(fd_); + fd_ = -1; +} + +/** + * \fn V4L2Subdevice::deviceNode() + * \brief Retrieve the path of the device node associated with the subdevice + * + * \return The subdevice's device node system path + */ + +/** + * \brief Set a crop rectangle on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the rectangle is to be applied to + * \param[inout] rect The rectangle describing crop target area + * + * \return 0 on success, or a negative error code otherwise + */ +int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect) +{ + return setSelection(pad, V4L2_SEL_TGT_CROP, rect); +} + +/** + * \brief Set a compose rectangle on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the rectangle is to be applied to + * \param[inout] rect The rectangle describing the compose target area + * + * \return 0 on success, or a negative error code otherwise + */ +int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect) +{ + return setSelection(pad, V4L2_SEL_TGT_COMPOSE, rect); +} + +/** + * \brief Retrieve the image format set on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the format is to be retrieved from + * \param[out] format The image bus format + * + * \return 0 for success, a negative error code otherwise + */ +int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format) +{ + struct v4l2_subdev_format subdevFmt = {}; + subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.pad = pad; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt); + if (ret) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Unable to get format on pad " << pad + << " of " << deviceNode_ << ": " << strerror(-ret); + return ret; + } + + format->width = subdevFmt.format.width; + format->height = subdevFmt.format.height; + format->mbus_code = subdevFmt.format.code; + + return 0; +} + +/** + * \brief Set an image format on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the format is to be applied to + * \param[inout] format The image bus format to apply to the subdevice's pad + * + * Apply the requested image format to the desired media pad and return the + * actually applied format parameters, as \ref V4L2Subdevice::getFormat would + * do. + * + * \return 0 for success, a negative error code otherwise + */ +int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format) +{ + struct v4l2_subdev_format subdevFmt = {}; + subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.pad = pad; + subdevFmt.format.width = format->width; + subdevFmt.format.height = format->height; + subdevFmt.format.code = format->mbus_code; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt); + if (ret) { + ret = -errno; + LOG(Error) << "Unable to set format: " << strerror(-ret); + return ret; + } + + format->width = subdevFmt.format.width; + format->height = subdevFmt.format.height; + format->mbus_code = subdevFmt.format.code; + + return 0; +} + +int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target, + Rectangle *rect) +{ + struct v4l2_subdev_selection sel = {}; + + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.pad = pad; + sel.target = target; + sel.flags = 0; + + sel.r.left = rect->y; + sel.r.top = rect->x; + sel.r.width = rect->w; + sel.r.height = rect->h; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, sel); + if (ret < 0) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Unable to set rectangle " << target << " on pad " + << pad << " of " << deviceNode_ << ": " + << strerror(-ret); + return ret; + } + + return 0; +} + +} /* namespace libcamera */ From patchwork Wed Feb 6 06:08:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 528 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7848661022 for ; Wed, 6 Feb 2019 07:08:26 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1BD2741 for ; Wed, 6 Feb 2019 07:08:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433306; bh=XcWsS02nA1PgO/Kbaj7TQTam0DlOIYrF2Kd9aUvbrNU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Zg5S9Moit8942puuiLs7dN0VYa52vY4F7YMypzMQbZHy/35W09JdaTKLchoo/xUP2 oZj2WCThHTKMuu7dgOON5RcYbji3+EySCo7277ZbPs0SRXCVH2XsCtU4tfUdzwx/Fr lqzRoYWd25MgXsPdWtk27j3pfEZKKID1CN0ppcBA= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:04 +0200 Message-Id: <20190206060818.13907-14-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 13/27] libcamera: stream: Construct a stream 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:28 -0000 From: Kieran Bingham Construct a stream object with a default internal pool. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- include/libcamera/stream.h | 8 ++++++++ src/libcamera/stream.cpp | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index 890678360ee8..111f2c933efa 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -7,10 +7,18 @@ #ifndef __LIBCAMERA_STREAM_H__ #define __LIBCAMERA_STREAM_H__ +#include + namespace libcamera { class Stream final { +public: + Stream(); + BufferPool &bufferPool() { return bufferPool_; } + +private: + BufferPool bufferPool_; }; struct StreamConfiguration { diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index dd9092d958d9..b6238946a8d5 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -47,6 +47,24 @@ namespace libcamera { * optimal stream for the task. */ +/** + * \brief Construct a stream with default parameters + */ +Stream::Stream() +{ +} + +/** + * \fn Stream::bufferPool() + * \brief Retrieve the buffer pool for the stream + * + * The buffer pool handles the buffers used to capture frames at the output of + * the stream. It is initially created empty and shall be populated with + * buffers before being used. + * + * \return A reference to the buffer pool + */ + /** * \struct StreamConfiguration * \brief Configuration parameters for a stream From patchwork Wed Feb 6 06:08:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 529 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DF08961031 for ; Wed, 6 Feb 2019 07:08:26 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5C5BE2D8 for ; Wed, 6 Feb 2019 07:08:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433306; bh=jgbIrKU/oRcUzCL3uuRTA+xd+WvGgv6wQGZZkBzYs7A=; h=From:To:Subject:Date:In-Reply-To:References:From; b=TyBOVN25Fd3HZs2XAQoaNbHTCcDQEybGgYErUvaiXQMkwEijE06VEapfzmh8x1/dU ac9nGrQsyqd1SN5Q8T2+mXRd4lQwws8DqlPssjKW/NUwb7OEhfYI9h5zXR8417mRhl vy0LHhTuxqx1hho2eWixHIDdh0zcpfHGH8CqrrDg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:05 +0200 Message-Id: <20190206060818.13907-15-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 14/27] libcamera: stream: Add stream configuration to the stream object 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:29 -0000 From: Niklas Söderlund Add a cache of the active stream configuration to the stream object. This cache is to be updated from the Camera object and can be accessed read only from both the application and pipeline handlers. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- include/libcamera/stream.h | 20 ++++++++---- src/libcamera/stream.cpp | 62 +++++++++++++++++++++----------------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index 111f2c933efa..3e8e83a2ff24 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -11,23 +11,31 @@ namespace libcamera { +class Camera; + +struct StreamConfiguration { + unsigned int width; + unsigned int height; + unsigned int pixelFormat; + + unsigned int bufferCount; +}; + class Stream final { public: Stream(); BufferPool &bufferPool() { return bufferPool_; } + const StreamConfiguration &configuration() const { return configuration_; } private: + friend Camera; + BufferPool bufferPool_; + StreamConfiguration configuration_; }; -struct StreamConfiguration { - unsigned int width; - unsigned int height; - unsigned int pixelFormat; - unsigned int bufferCount; -}; } /* namespace libcamera */ diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index b6238946a8d5..c4943c91b2e6 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -29,6 +29,37 @@ namespace libcamera { +/** + * \struct StreamConfiguration + * \brief Configuration parameters for a stream + * + * The StreamConfiguration structure models all information which can be + * configured for a single video stream. + */ + +/** + * \var StreamConfiguration::width + * \brief Stream width in pixels + */ + +/** + * \var StreamConfiguration::height + * \brief Stream height in pixels + */ + +/** + * \var StreamConfiguration::pixelFormat + * \brief Stream pixel format + * + * This is a little endian four character code representation of the pixel + * format described in V4L2 using the V4L2_PIX_FMT_* definitions. + */ + +/** + * \var StreamConfiguration::bufferCount + * \brief Requested number of buffers to allocate for the stream + */ + /** * \class Stream * \brief Video stream for a camera @@ -66,34 +97,9 @@ Stream::Stream() */ /** - * \struct StreamConfiguration - * \brief Configuration parameters for a stream - * - * The StreamConfiguration structure models all information which can be - * configured for a single video stream. - */ - -/** - * \var StreamConfiguration::width - * \brief Stream width in pixels - */ - -/** - * \var StreamConfiguration::height - * \brief Stream height in pixels - */ - -/** - * \var StreamConfiguration::pixelFormat - * \brief Stream pixel format - * - * This is a little endian four character code representation of the pixel - * format described in V4L2 using the V4L2_PIX_FMT_* definitions. - */ - -/** - * \var StreamConfiguration::bufferCount - * \brief Requested number of buffers to allocate for the stream + * \fn Stream::configuration() + * \brief Retrieve the active configuration of the stream + * \return The active configuration of the stream */ } /* namespace libcamera */ From patchwork Wed Feb 6 06:08:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 530 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 2E5E561020 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CAAE22DA for ; Wed, 6 Feb 2019 07:08:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433306; bh=Uj/kyqoFHyMJhqhmritCjWC5aEfPSffUiyNFq64um0c=; h=From:To:Subject:Date:In-Reply-To:References:From; b=uX1LY6LWACCRqS/y+keXBJfbhTSkNSTEsxQqVuzRCpFGm9S7taKkMJavhVLdfuxCc bknKEenE7fGK52s95nHgPLDKVlkY2y+HolaxXuuf0odjj0LXVgUYjExyJQkkCYVkpX UmsSZyVgLO9gk9CMzkusKZPRc7E/d3heZeLMbfbs= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:06 +0200 Message-Id: <20190206060818.13907-16-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 15/27] libcamera: Provide a Request object 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:29 -0000 From: Jacopo Mondi Implement a Request object used by applications to queue image capture requests to a camera. Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- include/libcamera/camera.h | 3 + include/libcamera/libcamera.h | 1 + include/libcamera/meson.build | 1 + include/libcamera/request.h | 44 ++++++++++++ src/libcamera/camera.cpp | 5 ++ src/libcamera/meson.build | 1 + src/libcamera/request.cpp | 122 ++++++++++++++++++++++++++++++++++ 7 files changed, 177 insertions(+) create mode 100644 include/libcamera/request.h create mode 100644 src/libcamera/request.cpp diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 4940c344440e..bbe2696e837b 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -11,10 +11,12 @@ #include #include +#include #include namespace libcamera { +class Buffer; class PipelineHandler; class Stream; class StreamConfiguration; @@ -31,6 +33,7 @@ public: const std::string &name() const; + Signal &> requestCompleted; Signal disconnected; int acquire(); diff --git a/include/libcamera/libcamera.h b/include/libcamera/libcamera.h index 8167e8099ac0..dda576e906fb 100644 --- a/include/libcamera/libcamera.h +++ b/include/libcamera/libcamera.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 8c14423bc444..5788e9bbdf3e 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -5,6 +5,7 @@ libcamera_api = files([ 'event_dispatcher.h', 'event_notifier.h', 'libcamera.h', + 'request.h', 'signal.h', 'stream.h', 'timer.h', diff --git a/include/libcamera/request.h b/include/libcamera/request.h new file mode 100644 index 000000000000..ef081177309f --- /dev/null +++ b/include/libcamera/request.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * request.h - Capture request handling + */ +#ifndef __LIBCAMERA_REQUEST_H__ +#define __LIBCAMERA_REQUEST_H__ + +#include +#include + +#include + +namespace libcamera { + +class Buffer; +class Camera; +class Stream; + +class Request +{ +public: + explicit Request(Camera *camera); + Request(const Request &) = delete; + Request &operator=(const Request &) = delete; + + int setBuffers(const std::map &streamMap); + Buffer *findBuffer(Stream *stream) const; + +private: + friend class Camera; + + int prepare(); + void bufferCompleted(Buffer *buffer); + + Camera *camera_; + std::map bufferMap_; + std::unordered_set pending_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_REQUEST_H__ */ diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index bcf3d54ab1c3..e8dab6f0bab2 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -97,6 +97,11 @@ const std::string &Camera::name() const return name_; } +/** + * \var Camera::requestCompleted + * \brief Signal emitted when a request queued to the camera has completed + */ + /** * \var Camera::disconnected * \brief Signal emitted when the camera is disconnected from the system diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index ac991dc59d18..c5354c136563 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -11,6 +11,7 @@ libcamera_sources = files([ 'media_device.cpp', 'media_object.cpp', 'pipeline_handler.cpp', + 'request.cpp', 'signal.cpp', 'stream.cpp', 'timer.cpp', diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp new file mode 100644 index 000000000000..922682a32188 --- /dev/null +++ b/src/libcamera/request.cpp @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * request.cpp - Capture request handling + */ + +#include + +#include +#include +#include +#include + +#include "log.h" + +/** + * \file request.h + * \brief Describes a frame capture request to be processed by a camera + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Request) + +/** + * \class Request + * \brief A frame capture request + * + * A Request allows an application to associate buffers and controls on a + * per-frame basis to be queued to the camera device for processing. + */ + +/** + * \brief Create a capture request for a camera + * \param[in] camera The camera that creates the request + */ +Request::Request(Camera *camera) + : camera_(camera) +{ +} + +/** + * \brief Set the streams to capture with associated buffers + * \param[in] streamMap The map of streams to buffers + * \return 0 on success or a negative error code otherwise + * \retval -EBUSY Buffers have already been set + */ +int Request::setBuffers(const std::map &streamMap) +{ + if (!bufferMap_.empty()) { + LOG(Request, Error) << "Buffers already set"; + return -EBUSY; + } + + bufferMap_ = streamMap; + return 0; +} + +/** + * \var Request::bufferMap_ + * \brief Mapping of streams to buffers for this request + * + * The bufferMap_ tracks the buffers associated with each stream. If a stream is + * not utilised in this request there will be no buffer for that stream in the + * map. + */ + +/** + * \brief Return the buffer associated with a stream + * \param[in] stream The stream the buffer is associated to + * + * \return The buffer associated with the stream, or nullptr if the stream is + * not part of this request + */ +Buffer *Request::findBuffer(Stream *stream) const +{ + auto it = bufferMap_.find(stream); + if (it == bufferMap_.end()) + return nullptr; + + return it->second; +} + +/** + * \brief Prepare the resources for the completion handler + */ +int Request::prepare() +{ + for (auto const &pair : bufferMap_) { + Buffer *buffer = pair.second; + buffer->completed.connect(this, &Request::bufferCompleted); + pending_.insert(buffer); + } + + return 0; +} + +/** + * \brief Slot for the buffer completed signal + * + * The bufferCompleted method serves as slot where to connect the + * Buffer::completed signal that is emitted when a buffer has available + * data. + * + * The request completes when all the buffers it contains are ready to be + * presented to the application. + */ +void Request::bufferCompleted(Buffer *buffer) +{ + buffer->completed.disconnect(this, &Request::bufferCompleted); + + int ret = pending_.erase(buffer); + ASSERT(ret == 1); + + if (pending_.empty()) { + std::map buffers(std::move(bufferMap_)); + camera_->requestCompleted.emit(this, buffers); + } +} + +} /* namespace libcamera */ From patchwork Wed Feb 6 06:08:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 532 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 774FA61040 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1322A2D8 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433307; bh=kMAlJ1ARRsRLF0eLMB/RxFJ+jXDX7LjR6/9xqfScPWY=; h=From:To:Subject:Date:In-Reply-To:References:From; b=ZSpBEbXBCbqPBgJMyE1XKugtS+9+85hUoVor8Os/EtUFCpQHkdhFeZ6+sCZ36Yvip u62A85qusGg2NyaQmiHYe2lwizqubW06DRlWJp1WYNxXX+Fv8NznLmVoCxHWS5dIum T4SQuRDl342Lvhepd4hoOk9IvjadVHl4hKD4+Y0s= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:07 +0200 Message-Id: <20190206060818.13907-17-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 16/27] libcamera: pipeline_handler: Extend the interface to support capture 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:29 -0000 From: Niklas Söderlund In order to support capture, the pipeline handler needs methods to allocate and free buffers, to start and stop the capture and to queue requests. Define those interfaces in the PipelineHandler class and implement them as stubs in the existing pipeline handlers. This initial implementation only considers the allocation of new buffers. Future work would need to expand this to also cover importing buffers from an external source. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- src/libcamera/include/pipeline_handler.h | 10 ++++ src/libcamera/pipeline/ipu3/ipu3.cpp | 34 ++++++++++++ src/libcamera/pipeline/uvcvideo.cpp | 34 ++++++++++++ src/libcamera/pipeline/vimc.cpp | 34 ++++++++++++ src/libcamera/pipeline_handler.cpp | 67 ++++++++++++++++++++++++ 5 files changed, 179 insertions(+) diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index b4321f0fa0f7..7f2ec2975db0 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -14,10 +14,12 @@ namespace libcamera { +class BufferPool; class Camera; class CameraManager; class DeviceEnumerator; class MediaDevice; +class Request; class Stream; class StreamConfiguration; @@ -45,6 +47,14 @@ public: virtual int configureStreams(Camera *camera, std::map &config) = 0; + virtual int allocateBuffers(Camera *camera, Stream *stream) = 0; + virtual int freeBuffers(Camera *camera, Stream *stream) = 0; + + virtual int start(const Camera *camera) = 0; + virtual void stop(const Camera *camera) = 0; + + virtual int queueRequest(const Camera *camera, Request *request) = 0; + virtual bool match(DeviceEnumerator *enumerator) = 0; protected: diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index fa3c5224f6d5..3f096bf1251b 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -34,6 +34,14 @@ public: int configureStreams(Camera *camera, std::map &config) override; + int allocateBuffers(Camera *camera, Stream *stream) override; + int freeBuffers(Camera *camera, Stream *stream) override; + + int start(const Camera *camera) override; + void stop(const Camera *camera) override; + + int queueRequest(const Camera *camera, Request *request) override; + bool match(DeviceEnumerator *enumerator); private: @@ -104,6 +112,32 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera, return 0; } +int PipelineHandlerIPU3::allocateBuffers(Camera *camera, Stream *stream) +{ + return -ENOTRECOVERABLE; +} + +int PipelineHandlerIPU3::freeBuffers(Camera *camera, Stream *stream) +{ + return 0; +} + +int PipelineHandlerIPU3::start(const Camera *camera) +{ + LOG(IPU3, Error) << "TODO: start camera"; + return 0; +} + +void PipelineHandlerIPU3::stop(const Camera *camera) +{ + LOG(IPU3, Error) << "TODO: stop camera"; +} + +int PipelineHandlerIPU3::queueRequest(const Camera *camera, Request *request) +{ + return 0; +} + bool PipelineHandlerIPU3::match(DeviceEnumerator *enumerator) { DeviceMatch cio2_dm("ipu3-cio2"); diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index ecec4e659ac8..74bdf5c5aea5 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -30,6 +30,14 @@ public: int configureStreams(Camera *camera, std::map &config) override; + int allocateBuffers(Camera *camera, Stream *stream) override; + int freeBuffers(Camera *camera, Stream *stream) override; + + int start(const Camera *camera) override; + void stop(const Camera *camera) override; + + int queueRequest(const Camera *camera, Request *request) override; + bool match(DeviceEnumerator *enumerator); private: @@ -82,6 +90,32 @@ int PipelineHandlerUVC::configureStreams(Camera *camera, return 0; } +int PipelineHandlerUVC::allocateBuffers(Camera *camera, Stream *stream) +{ + return -ENOTRECOVERABLE; +} + +int PipelineHandlerUVC::freeBuffers(Camera *camera, Stream *stream) +{ + return 0; +} + +int PipelineHandlerUVC::start(const Camera *camera) +{ + LOG(UVC, Error) << "TODO: start camera"; + return 0; +} + +void PipelineHandlerUVC::stop(const Camera *camera) +{ + LOG(UVC, Error) << "TODO: stop camera"; +} + +int PipelineHandlerUVC::queueRequest(const Camera *camera, Request *request) +{ + return 0; +} + bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator) { DeviceMatch dm("uvcvideo"); diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 86a89c63fab1..ea6f28c64cc0 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -30,6 +30,14 @@ public: int configureStreams(Camera *camera, std::map &config) override; + int allocateBuffers(Camera *camera, Stream *stream) override; + int freeBuffers(Camera *camera, Stream *stream) override; + + int start(const Camera *camera) override; + void stop(const Camera *camera) override; + + int queueRequest(const Camera *camera, Request *request) override; + bool match(DeviceEnumerator *enumerator); private: @@ -77,6 +85,32 @@ int PipeHandlerVimc::configureStreams(Camera *camera, return 0; } +int PipeHandlerVimc::allocateBuffers(Camera *camera, Stream *stream) +{ + return -ENOTRECOVERABLE; +} + +int PipeHandlerVimc::freeBuffers(Camera *camera, Stream *stream) +{ + return 0; +} + +int PipeHandlerVimc::start(const Camera *camera) +{ + LOG(VIMC, Error) << "TODO: start camera"; + return 0; +} + +void PipeHandlerVimc::stop(const Camera *camera) +{ + LOG(VIMC, Error) << "TODO: stop camera"; +} + +int PipeHandlerVimc::queueRequest(const Camera *camera, Request *request) +{ + return 0; +} + bool PipeHandlerVimc::match(DeviceEnumerator *enumerator) { DeviceMatch dm("vimc"); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 3a560e10c442..cc2d4643ec2f 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -109,6 +109,73 @@ PipelineHandler::~PipelineHandler() * \return 0 on success or a negative error code on error. */ +/** + * \fn PipelineHandler::allocateBuffers() + * \brief Allocate buffers for a stream + * \param[in] camera The camera the \a stream belongs to + * \param[in] stream The stream to allocate buffers for + * + * This method allocates buffers internally in the pipeline handler and + * associates them with the stream's buffer pool. + * + * The intended caller of this method is the Camera class. + * + * \return 0 on success or a negative error code on error + */ + +/** + * \fn PipelineHandler::freeBuffers() + * \brief Free all buffers associated with a stream + * \param[in] camera The camera the \a stream belongs to + * \param[in] stream The stream to free buffers from + * + * After a capture session has been stopped all buffers associated with the + * stream shall be freed. + * + * The intended caller of this method is the Camera class. + * + * \return 0 on success or a negative error code on error + */ + +/** + * \fn PipelineHandler::start() + * \brief Start capturing from a group of streams + * \param[in] camera The camera to start + * + * Start the group of streams that have been configured for capture by + * \a configureStreams(). The intended caller of this method is the Camera + * class which will in turn be called from the application to indicate that it + * has configured the streams and is ready to capture. + * + * \return 0 on success or a negative error code on error + */ + +/** + * \fn PipelineHandler::stop() + * \brief Stop capturing from all running streams + * \param[in] camera The camera to stop + * + * This method stops capturing and processing requests immediately. All pending + * requests are cancelled and complete immediately in an error state. + * + * \todo Complete the pending requests immediately + */ + +/** + * \fn PipelineHandler::queueRequest() + * \brief Queue a request to the camera + * \param[in] camera The camera to queue the request to + * \param[in] request The request to queue + * + * This method queues a capture request to the pipeline handler for processing. + * The request contains a set of buffers associated with streams and a set of + * parameters. The pipeline handler shall program the device to ensure that the + * parameters will be applied to the frames captured in the buffers provided in + * the request. + * + * \return 0 on success or a negative error code on error + */ + /** * \fn PipelineHandler::match(DeviceEnumerator *enumerator) * \brief Match media devices and create camera instances From patchwork Wed Feb 6 06:08:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 531 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AA7B56101F for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4DA5B2D7 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433307; bh=Ae3SwmEM8GyAopNtp+dAlcyU3evcdUf6DpcGsF2unv8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HuEZMh1ORf4aeUc3F3/Wk9tFOvS2tcIhTcMEQ5h0nCcLtaB68z1hNmGEMGmNKpMyY 9ava9FWx/7LxRDEO2fcY3uIDVUsILPsVHu6/BBH7uj2v8ZsroCbua5Gc8IFYcF1vNH sbC14R8+6JWyS5eRqakBZsqK32p/qDu2le0fu1UQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:08 +0200 Message-Id: <20190206060818.13907-18-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 17/27] libcamera: camera: Add helper to check for exclusive access 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:29 -0000 From: Niklas Söderlund Some operations on the camera requires the application to have exclusive access to the camera. To help check for this in these operations add a helper. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- include/libcamera/camera.h | 1 + src/libcamera/camera.cpp | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index bbe2696e837b..36bf1cbb215b 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -50,6 +50,7 @@ private: friend class PipelineHandler; void disconnect(); + int exclusiveAccess(); std::shared_ptr pipe_; std::string name_; diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index e8dab6f0bab2..62291d2c9e6c 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -240,6 +240,23 @@ Camera::streamConfiguration(std::vector &streams) * \retval -EINVAL The configuration is not valid */ int Camera::configureStreams(std::map &config) +{ + int ret; + + ret = exclusiveAccess(); + if (ret) + return ret; + + if (!config.size()) { + LOG(Camera, Error) + << "Can't configure streams without a configuration"; + return -EINVAL; + } + + return pipe_->configureStreams(this, config); +} + +int Camera::exclusiveAccess() { if (disconnected_) return -ENODEV; @@ -247,10 +264,7 @@ int Camera::configureStreams(std::map &config) if (!acquired_) return -EACCES; - if (!config.size()) - return -EINVAL; - - return pipe_->configureStreams(this, config); + return 0; } } /* namespace libcamera */ From patchwork Wed Feb 6 06:08:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 533 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DA08161047 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 865C22D9 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433307; bh=uPiFFS2vvE9G9XTdMsPnGoZdY/fhrPgCLtxrFCH/fBo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HMLSff5Cbtrg+8dc92xy49Qyl4/bHksRbqWqvSjfIY2QbYVNsi7H/vp9ACXCoSKWQ RkuRCoG6RqlJ9KSD/vRV0RLUsVtSjgtc2YtCjUFMoTWJJAt+IR3WWhlBd6I7EhfRYJ ZMa4P4rGrMelOcfbOdOZq3FNYsiQieEO+xFdEIao= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:09 +0200 Message-Id: <20190206060818.13907-19-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 18/27] libcamera: camera: Cache the stream configuration in the stream object 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:29 -0000 From: Niklas Söderlund The API towards the application and pipeline handler can be simplified if the camera caches which streams have been selected and their respective configuration. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- include/libcamera/camera.h | 1 + src/libcamera/camera.cpp | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 36bf1cbb215b..1c0ee07c2a22 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -55,6 +55,7 @@ private: std::shared_ptr pipe_; std::string name_; std::vector streams_; + std::vector activeStreams_; bool acquired_; bool disconnected_; diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 62291d2c9e6c..3f7b805b09a2 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -253,7 +253,20 @@ int Camera::configureStreams(std::map &config) return -EINVAL; } - return pipe_->configureStreams(this, config); + ret = pipe_->configureStreams(this, config); + if (ret) + return ret; + + activeStreams_.clear(); + for (auto const &iter : config) { + Stream *stream = iter.first; + const StreamConfiguration &cfg = iter.second; + + stream->configuration_ = cfg; + activeStreams_.push_back(stream); + } + + return 0; } int Camera::exclusiveAccess() From patchwork Wed Feb 6 06:08:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 534 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 240986103A for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BECE041 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433307; bh=/HHwNwFor12mfYEAbRtgdJcRixGtPiMri6J+aFj1QpY=; h=From:To:Subject:Date:In-Reply-To:References:From; b=QErC2OF+ARZp7G/gcboKiWhf9rBMF5JFFF/2KRGhQhW/9vCucb8TtNZXGA6snXHQE abwVnORJPU0HyRzO59tvsYD0BStcRRDSJgA5Ub0cA9rQ8aNcWm3eXTuiR1/E9K6ihX YLldUNdWx/6+p3K9iCaQpTXwmrBBg+rUn6wMT/Hk= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:10 +0200 Message-Id: <20190206060818.13907-20-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 19/27] libcamera: camera: Extend the interface to support capture 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:29 -0000 From: Niklas Söderlund In order to support capture, the camera needs methods to allocate and free buffers, to start and stop the capture and to queue requests. Define those interfaces in the Camera class and implement them to call the corresponding pipeline handler methods. Once a camera is started the pipeline handler of the camera will begin processing requests queued to the camera by the application until it gets stopped. Once a request is created it can be queued to the camera and the application will be notified asynchronously once the request is completed and be able to process all the buffers involved in the request. At this point the request objects don't support controls. This will be extended in the future. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- include/libcamera/camera.h | 10 +++ src/libcamera/camera.cpp | 141 +++++++++++++++++++++++++++++++++++++ src/libcamera/request.cpp | 13 ++-- 3 files changed, 159 insertions(+), 5 deletions(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 1c0ee07c2a22..bf70255a6a5e 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -18,6 +18,7 @@ namespace libcamera { class Buffer; class PipelineHandler; +class Request; class Stream; class StreamConfiguration; @@ -44,6 +45,15 @@ public: streamConfiguration(std::vector &streams); int configureStreams(std::map &config); + int allocateBuffers(); + void freeBuffers(); + + Request *createRequest(); + int queueRequest(Request *request); + + int start(); + int stop(); + private: Camera(PipelineHandler *pipe, const std::string &name); ~Camera(); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 3f7b805b09a2..1acb399c80a6 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include "log.h" @@ -264,11 +265,151 @@ int Camera::configureStreams(std::map &config) stream->configuration_ = cfg; activeStreams_.push_back(stream); + + /* + * Allocate buffer objects in the pool. + * Memory will be allocated and assigned later. + */ + stream->bufferPool().createBuffers(cfg.bufferCount); } return 0; } +/** + * \brief Allocate buffers for all configured streams + * \return 0 on success or a negative error code otherwise + */ +int Camera::allocateBuffers() +{ + int ret; + + ret = exclusiveAccess(); + if (ret) + return ret; + + if (activeStreams_.empty()) { + LOG(Camera, Error) + << "Can't allocate buffers without streams"; + return -EINVAL; + } + + for (Stream *stream : activeStreams_) { + ret = pipe_->allocateBuffers(this, stream); + if (ret) { + LOG(Camera, Error) << "Failed to allocate buffers"; + freeBuffers(); + return ret; + } + } + + return 0; +} + +/** + * \brief Release all buffers from allocated pools in each stream + */ +void Camera::freeBuffers() +{ + for (Stream *stream : activeStreams_) { + if (!stream->bufferPool().count()) + continue; + + pipe_->freeBuffers(this, stream); + stream->bufferPool().destroyBuffers(); + } +} + +/** + * \brief Create a request object for the camera + * + * This method creates an empty request for the application to fill with + * buffers and paramaters, and queue for capture. + * + * The ownership of the returned request is passed to the caller, which is + * responsible for either queueing the request or deleting it. + * + * \return A pointer to the newly created request, or nullptr on error + */ +Request *Camera::createRequest() +{ + if (exclusiveAccess()) + return nullptr; + + return new Request(this); +} + +/** + * \brief Queue a request to the camera + * \param[in] request The request to queue to the camera + * + * This method queues a \a request allocated with createRequest() to the camera + * for capture. Once the request has been queued, the camera will notify its + * completion through the \ref requestCompleted signal. + * + * Ownership of the request is transferred to the camera. It will be deleted + * automatically after it completes. + * + * \return 0 on success or a negative error code on error + */ +int Camera::queueRequest(Request *request) +{ + int ret; + + ret = exclusiveAccess(); + if (ret) + return ret; + + ret = request->prepare(); + if (ret) { + LOG(Camera, Error) << "Failed to prepare request"; + return ret; + } + + return pipe_->queueRequest(this, request); +} + +/** + * \brief Start capture from camera + * + * Start the camera capture session. Once the camera is started the application + * can queue requests to the camera to process and return to the application + * until the capture session is terminated with \a stop(). + * + * \return 0 on success or a negative error code on error + */ +int Camera::start() +{ + int ret = exclusiveAccess(); + if (ret) + return ret; + + LOG(Camera, Debug) << "Starting capture"; + + return pipe_->start(this); +} + +/** + * \brief Stop capture from camera + * + * This method stops capturing and processing requests immediately. All pending + * requests are cancelled and complete synchronously in an error state. + * + * \return 0 on success or a negative error code on error + */ +int Camera::stop() +{ + int ret = exclusiveAccess(); + if (ret) + return ret; + + LOG(Camera, Debug) << "Stopping capture"; + + pipe_->stop(this); + + return 0; +} + int Camera::exclusiveAccess() { if (disconnected_) diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 922682a32188..d76db24de0e2 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -104,7 +104,8 @@ int Request::prepare() * data. * * The request completes when all the buffers it contains are ready to be - * presented to the application. + * presented to the application. It then emits the Camera::requestCompleted + * signal and is automatically deleted. */ void Request::bufferCompleted(Buffer *buffer) { @@ -113,10 +114,12 @@ void Request::bufferCompleted(Buffer *buffer) int ret = pending_.erase(buffer); ASSERT(ret == 1); - if (pending_.empty()) { - std::map buffers(std::move(bufferMap_)); - camera_->requestCompleted.emit(this, buffers); - } + if (!pending_.empty()) + return; + + std::map buffers(std::move(bufferMap_)); + camera_->requestCompleted.emit(this, buffers); + delete this; } } /* namespace libcamera */ From patchwork Wed Feb 6 06:08:11 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 535 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5E12B61048 for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0B43F2D9 for ; Wed, 6 Feb 2019 07:08:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433308; bh=RKXY6CJrIGea4sPWYGzQGXfcjVU1phDzf+yLipwbUn4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HWX91i+mybB7eTstLxl6F9ejWKZ2uWVNXFfTe9I8XN0m4ICsB7Rh0yji0gXrPpjDz 9ezBIjxhAgMkgKDi8Uf6v3TraA7/g91P7Byt7h/VzofxnZ2hlE2OE+MmedsuIgVsY4 MKo/A4nASObhqaxHQb245XcuftNIT2z7TmKQnhDc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:11 +0200 Message-Id: <20190206060818.13907-21-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 20/27] libcamera: pipeline: uvcvideo: Implement capture support 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:30 -0000 From: Kieran Bingham Replace the buffer allocation, capture start/stop and request queue stubs with real implementations. Signed-off-by: Kieran Bingham Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart --- src/libcamera/pipeline/uvcvideo.cpp | 35 ++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index 74bdf5c5aea5..fc31c52c0ecd 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include "device_enumerator.h" @@ -84,35 +85,53 @@ int PipelineHandlerUVC::configureStreams(Camera *camera, { StreamConfiguration *cfg = &config[&stream_]; - LOG(UVC, Info) << "TODO: Configure the camera for resolution " - << cfg->width << "x" << cfg->height; + LOG(UVC, Debug) << "Configure the camera for resolution " + << cfg->width << "x" << cfg->height; - return 0; + V4L2DeviceFormat format = {}; + format.width = cfg->width; + format.height = cfg->height; + format.fourcc = cfg->pixelFormat; + + return video_->setFormat(&format); } int PipelineHandlerUVC::allocateBuffers(Camera *camera, Stream *stream) { - return -ENOTRECOVERABLE; + const StreamConfiguration &cfg = stream->configuration(); + + LOG(UVC, Debug) << "Requesting " << cfg.bufferCount << " buffers"; + + return video_->exportBuffers(cfg.bufferCount, &stream->bufferPool()); } int PipelineHandlerUVC::freeBuffers(Camera *camera, Stream *stream) { - return 0; + return video_->releaseBuffers(); } int PipelineHandlerUVC::start(const Camera *camera) { - LOG(UVC, Error) << "TODO: start camera"; - return 0; + return video_->streamOn(); } void PipelineHandlerUVC::stop(const Camera *camera) { - LOG(UVC, Error) << "TODO: stop camera"; + video_->streamOff(); } int PipelineHandlerUVC::queueRequest(const Camera *camera, Request *request) { + Buffer *buffer = request->findBuffer(&stream_); + if (!buffer) { + LOG(UVC, Error) + << "Attempt to queue request with invalid stream"; + + return -ENOENT; + } + + video_->queueBuffer(buffer); + return 0; } From patchwork Wed Feb 6 06:08:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 536 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 9796D61022 for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 42FA62D8 for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433308; bh=OxR2dVUYzTcPjktGWfvQKEVJbM1cGNhPqnsRzEdtwg8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=jj9MSK/vMVxzcRLn8on8at3AKH4PkheOlrbOoL1qBLCAkF74ULZc64Xzm45AwzHs+ elNWsgrlMqq6AZk+7uLd0ihJwQRqtJGpAFDyR9TAfM4W3C4Zab/b8DomqfDjw0v4ij PBSmXMSRIJqEDgHTLK/bXL14vOE1lmDi2T0AaFRk= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:12 +0200 Message-Id: <20190206060818.13907-22-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 21/27] libcamera: pipeline: vimc: Set a default format 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:30 -0000 From: Niklas Söderlund Pick a default format for the one stream in a vimc camera. This is just a starting point to define a good default format for the vimc camera, and is expected to evolve over time as the capabilities of the library grows. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- src/libcamera/pipeline/vimc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index ea6f28c64cc0..9c0406bef9dc 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -67,7 +67,11 @@ PipeHandlerVimc::streamConfiguration(Camera *camera, StreamConfiguration config{}; - LOG(VIMC, Info) << "TODO: Return a good default format"; + LOG(VIMC, Debug) << "Retrieving default format"; + config.width = 640; + config.height = 480; + config.pixelFormat = V4L2_PIX_FMT_RGB24; + config.bufferCount = 4; configs[&stream_] = config; From patchwork Wed Feb 6 06:08:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 537 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 CFBB76104A for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7C8A62D9 for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433308; bh=5JOE8bvH20QvrILDhnKB9gtX6Y5bWWTSaiqR7FsSGVE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=igynAZmpVdjz5PtGfIxKtxhZyA+k7T7KoyubjPVJOQtj0uqq3nxInNEYl59QYaX58 uTwo+zQKXie0L3WSl5XwUBFVWcKvqSt9GvKGDIeRaEParAAuRETasYFnZMGiUxfbJO EcQ+qGwtxcMXpilVivJbFBy8OPwHWzhoynFutUFU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:13 +0200 Message-Id: <20190206060818.13907-23-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 22/27] libcamera: pipeline: vimc: Implement capture support 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:30 -0000 From: Niklas Söderlund Replace the buffer allocation, capture start/stop and request queue stubs with real implementations. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- src/libcamera/pipeline/vimc.cpp | 35 +++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 9c0406bef9dc..0e9ad7b59ee5 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include "device_enumerator.h" @@ -83,35 +84,53 @@ int PipeHandlerVimc::configureStreams(Camera *camera, { StreamConfiguration *cfg = &config[&stream_]; - LOG(VIMC, Info) << "TODO: Configure the camera for resolution " - << cfg->width << "x" << cfg->height; + LOG(VIMC, Debug) << "Configure the camera for resolution " + << cfg->width << "x" << cfg->height; - return 0; + V4L2DeviceFormat format = {}; + format.width = cfg->width; + format.height = cfg->height; + format.fourcc = cfg->pixelFormat; + + return video_->setFormat(&format); } int PipeHandlerVimc::allocateBuffers(Camera *camera, Stream *stream) { - return -ENOTRECOVERABLE; + const StreamConfiguration &cfg = stream->configuration(); + + LOG(VIMC, Debug) << "Requesting " << cfg.bufferCount << " buffers"; + + return video_->exportBuffers(cfg.bufferCount, &stream->bufferPool()); } int PipeHandlerVimc::freeBuffers(Camera *camera, Stream *stream) { - return 0; + return video_->releaseBuffers(); } int PipeHandlerVimc::start(const Camera *camera) { - LOG(VIMC, Error) << "TODO: start camera"; - return 0; + return video_->streamOn(); } void PipeHandlerVimc::stop(const Camera *camera) { - LOG(VIMC, Error) << "TODO: stop camera"; + video_->streamOff(); } int PipeHandlerVimc::queueRequest(const Camera *camera, Request *request) { + Buffer *buffer = request->findBuffer(&stream_); + if (!buffer) { + LOG(VIMC, Error) + << "Attempt to queue request with invalid stream"; + + return -ENOENT; + } + + video_->queueBuffer(buffer); + return 0; } From patchwork Wed Feb 6 06:08:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 538 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 1303D6102A for ; Wed, 6 Feb 2019 07:08:29 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B4B942D7 for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433308; bh=BRNAza+w9JzDD6WKA/TZlvelODYw38XZ+wNVJP90JK4=; h=From:To:Subject:Date:In-Reply-To:References:From; b=rqTEbixDU4UoN3tZdQPCbOZ3frp4a/rM80mEm7L4eGXnw5XXfXLxMyJ0gRnljQAuh H5libZTXOPRyORUiT3lEiN40UyTS3QskOVIqcg+sjAkMUSpFmeokjusUribWPD/Zvk s0pLrUBohxoV1ghZ/0LOJ5YZ0Ov3TW9CLzl2lkH4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:14 +0200 Message-Id: <20190206060818.13907-24-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 23/27] libcamera: pipeline: ipu3: Create video devices and subdevices 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:30 -0000 From: Jacopo Mondi Create the video devices and subdevices associated with an IPU3 camera. While at there, move the IPU3 pipeline handler class definition and the associated IPU3CameraData to a separate header as the class has now grown enough. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/libcamera/pipeline/ipu3/ipu3.cpp | 76 ++++++++++++++++------------ 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 3f096bf1251b..9629057a1b2f 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -17,6 +17,7 @@ #include "pipeline_handler.h" #include "utils.h" #include "v4l2_device.h" +#include "v4l2_subdevice.h" namespace libcamera { @@ -49,23 +50,32 @@ private: { public: IPU3CameraData() - : dev_(nullptr) {} - ~IPU3CameraData() { delete dev_; } - V4L2Device *dev_; + : cio2_(nullptr), csi2_(nullptr), sensor_(nullptr) {} + + ~IPU3CameraData() + { + delete cio2_; + delete csi2_; + delete sensor_; + } + + V4L2Device *cio2_; + V4L2Subdevice *csi2_; + V4L2Subdevice *sensor_; + Stream stream_; }; - std::shared_ptr cio2_; - std::shared_ptr imgu_; - IPU3CameraData *cameraData(const Camera *camera) { return static_cast( PipelineHandler::cameraData(camera)); } - V4L2Device *createVideoDevice(unsigned int id); void registerCameras(); + + std::shared_ptr cio2_; + std::shared_ptr imgu_; }; PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager) @@ -208,24 +218,6 @@ error_release_mdev: return false; } -/* Create video devices for the CIO2 unit associated with a camera. */ -V4L2Device *PipelineHandlerIPU3::createVideoDevice(unsigned int id) -{ - std::string cio2Name = "ipu3-cio2 " + std::to_string(id); - MediaEntity *cio2 = cio2_->getEntityByName(cio2Name); - if (!cio2) - return nullptr; - - V4L2Device *dev = new V4L2Device(cio2); - if (dev->open()) { - delete dev; - return nullptr; - } - dev->close(); - - return dev; -} - /* * Cameras are created associating an image sensor (represented by a * media entity with function MEDIA_ENT_F_CAM_SENSOR) to one of the four @@ -241,6 +233,7 @@ void PipelineHandlerIPU3::registerCameras() for (unsigned int id = 0; id < 4; ++id) { std::string csi2Name = "ipu3-csi2 " + std::to_string(id); MediaEntity *csi2 = cio2_->getEntityByName(csi2Name); + int ret; /* * This shall not happen, as the device enumerator matched @@ -281,18 +274,37 @@ void PipelineHandlerIPU3::registerCameras() std::shared_ptr camera = Camera::create(this, cameraName, streams); /* - * If V4L2 device creation fails, the Camera instance won't be - * registered. The 'camera' shared pointer goes out of scope - * and deletes the Camera it manages. + * Create and open video devices and subdevices associated with + * the camera. + * + * If any of these operations fails, the Camera instance won't + * be registered. The 'camera' shared pointer and the 'data' + * unique pointers go out of scope and delete the objects they + * manage. */ - data->dev_ = createVideoDevice(id); - if (!data->dev_) { + std::string cio2Name = "ipu3-cio2 " + std::to_string(id); + MediaEntity *cio2 = cio2_->getEntityByName(cio2Name); + if (!cio2) { LOG(IPU3, Error) - << "Failed to register camera[" - << numCameras << "] \"" << cameraName << "\""; + << "Failed to get entity '" << cio2Name << "'"; continue; } + data->cio2_ = new V4L2Device(cio2); + ret = data->cio2_->open(); + if (ret) + continue; + + data->sensor_ = new V4L2Subdevice(sensor); + ret = data->sensor_->open(); + if (ret) + continue; + + data->csi2_ = new V4L2Subdevice(csi2); + ret = data->csi2_->open(); + if (ret) + continue; + setCameraData(camera.get(), std::move(data)); registerCamera(std::move(camera)); From patchwork Wed Feb 6 06:08:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 539 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 4C84A6102E for ; Wed, 6 Feb 2019 07:08:29 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EA75D2D8 for ; Wed, 6 Feb 2019 07:08:28 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433309; bh=MDucwjuFwsuJIjZgyjkqHrLzRxfhWJSMRbAeJMD74Pw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=oiv0D6Z0feX72kirK/N23RIFNUJaxLztgZ1c3qC+RbRnuvLZct0TYW/a7n4XeLSpQ Ounr+jBMcec/A1GPO4duXstrLQmNSVQQdKXvF5uI80G0M+7IjIoWUHHVukVFIGifvR 6k+fq0KPop98a8z6ZUWDnzKVgs7kgBW4aac2YZQU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:15 +0200 Message-Id: <20190206060818.13907-25-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 24/27] libcamera: pipeline: ipu3: Implement capture support 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:30 -0000 From: Jacopo Mondi Replace the buffer allocation, capture start/stop and request queue stubs with real implementations. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Reviewed-by: Kieran Bingham Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/libcamera/pipeline/ipu3/ipu3.cpp | 123 +++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 9 deletions(-) diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 9629057a1b2f..34b03995ae31 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include "device_enumerator.h" @@ -97,12 +98,23 @@ PipelineHandlerIPU3::streamConfiguration(Camera *camera, std::vector &streams) { IPU3CameraData *data = cameraData(camera); - std::map configs; + V4L2SubdeviceFormat format = {}; - StreamConfiguration config{}; + /* + * FIXME: As of now, return the image format reported by the sensor. + * In future good defaults should be provided for each stream. + */ + if (data->sensor_->getFormat(0, &format)) { + LOG(IPU3, Error) << "Failed to create stream configurations"; + return configs; + } - LOG(IPU3, Info) << "TODO: Return a good default format"; + StreamConfiguration config = {}; + config.width = format.width; + config.height = format.height; + config.pixelFormat = V4L2_PIX_FMT_IPU3_SGRBG10; + config.bufferCount = 4; configs[&data->stream_] = config; @@ -113,38 +125,131 @@ int PipelineHandlerIPU3::configureStreams(Camera *camera, std::map &config) { IPU3CameraData *data = cameraData(camera); - StreamConfiguration *cfg = &config[&data->stream_]; + V4L2Subdevice *sensor = data->sensor_; + V4L2Subdevice *csi2 = data->csi2_; + V4L2Device *cio2 = data->cio2_; + V4L2SubdeviceFormat subdevFormat = {}; + V4L2DeviceFormat devFormat = {}; + int ret; - LOG(IPU3, Info) << "TODO: Configure the camera for resolution " - << cfg->width << "x" << cfg->height; + /* + * FIXME: as of now, the format gets applied to the sensor and is + * propagated along the pipeline. It should instead be applied on the + * capture device and the sensor format calculated accordingly. + */ + + ret = sensor->getFormat(0, &subdevFormat); + if (ret) + return ret; + + subdevFormat.width = cfg->width; + subdevFormat.height = cfg->height; + ret = sensor->setFormat(0, &subdevFormat); + if (ret) + return ret; + + /* Return error if the requested format cannot be applied to sensor. */ + if (subdevFormat.width != cfg->width || + subdevFormat.height != cfg->height) { + LOG(IPU3, Error) + << "Failed to apply image format " + << subdevFormat.width << "x" << subdevFormat.height + << " - got: " << cfg->width << "x" << cfg->height; + return -EINVAL; + } + + ret = csi2->setFormat(0, &subdevFormat); + if (ret) + return ret; + + ret = cio2->getFormat(&devFormat); + if (ret) + return ret; + + devFormat.width = subdevFormat.width; + devFormat.height = subdevFormat.height; + devFormat.fourcc = cfg->pixelFormat; + + ret = cio2->setFormat(&devFormat); + if (ret) + return ret; + + LOG(IPU3, Info) << cio2->driverName() << ": " + << devFormat.width << "x" << devFormat.height + << "- 0x" << std::hex << devFormat.fourcc << " planes: " + << devFormat.planes; return 0; } int PipelineHandlerIPU3::allocateBuffers(Camera *camera, Stream *stream) { - return -ENOTRECOVERABLE; + IPU3CameraData *data = cameraData(camera); + const StreamConfiguration &cfg = stream->configuration(); + + if (!cfg.bufferCount) + return -EINVAL; + + int ret = data->cio2_->exportBuffers(cfg.bufferCount, + &stream->bufferPool()); + if (ret) { + LOG(IPU3, Error) << "Failed to request memory"; + return ret; + } + + return 0; } int PipelineHandlerIPU3::freeBuffers(Camera *camera, Stream *stream) { + IPU3CameraData *data = cameraData(camera); + + int ret = data->cio2_->releaseBuffers(); + if (ret) { + LOG(IPU3, Error) << "Failed to release memory"; + return ret; + } + return 0; } int PipelineHandlerIPU3::start(const Camera *camera) { - LOG(IPU3, Error) << "TODO: start camera"; + IPU3CameraData *data = cameraData(camera); + int ret; + + ret = data->cio2_->streamOn(); + if (ret) { + LOG(IPU3, Info) << "Failed to start camera " << camera->name(); + return ret; + } + return 0; } void PipelineHandlerIPU3::stop(const Camera *camera) { - LOG(IPU3, Error) << "TODO: stop camera"; + IPU3CameraData *data = cameraData(camera); + + if (data->cio2_->streamOff()) + LOG(IPU3, Info) << "Failed to stop camera " << camera->name(); } int PipelineHandlerIPU3::queueRequest(const Camera *camera, Request *request) { + IPU3CameraData *data = cameraData(camera); + Stream *stream = &data->stream_; + + Buffer *buffer = request->findBuffer(stream); + if (!buffer) { + LOG(IPU3, Error) + << "Attempt to queue request with invalid stream"; + return -ENOENT; + } + + data->cio2_->queueBuffer(buffer); + return 0; } From patchwork Wed Feb 6 06:08:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 540 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 86BFB61031 for ; Wed, 6 Feb 2019 07:08:29 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3262441 for ; Wed, 6 Feb 2019 07:08:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433309; bh=EZ0uI2hILy2K6NEikaRZG1CpzfbRdZLF5+lgdsyEO6Q=; h=From:To:Subject:Date:In-Reply-To:References:From; b=MOvCcGmLTjTceq1g18AoWf0yPrgTd9qhFCHtsD6RsfqenzcXZq+k8CsznZhhQBDR6 iO1ec4R3bojvSCn4e6nULfCuZInvo/4DQ3aH2LvX5TDtOLZD2glqcEh273DG9KM5yx 0IlsJsjSAOq8xeMuAU3uSFCVq9V9QKePlFQm1tXI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:16 +0200 Message-Id: <20190206060818.13907-26-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 25/27] cam: Add capture operation 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:30 -0000 From: Niklas Söderlund Add an option to capture frames from a camera and keep it running until the user terminates by sending SIGINT. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- src/cam/main.cpp | 141 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 30 deletions(-) diff --git a/src/cam/main.cpp b/src/cam/main.cpp index 06f9e6e16e87..ec5040e19935 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -5,6 +5,7 @@ * main.cpp - cam - The libcamera swiss army knife */ +#include #include #include #include @@ -18,16 +19,17 @@ using namespace libcamera; OptionsParser::Options options; +std::shared_ptr camera; +EventLoop *loop; enum { OptCamera = 'c', + OptCapture = 'C', OptFormat = 'f', OptHelp = 'h', OptList = 'l', }; -EventLoop *loop; - void signalHandler(int signal) { std::cout << "Exiting" << std::endl; @@ -48,6 +50,8 @@ static int parseOptions(int argc, char *argv[]) parser.addOption(OptCamera, OptionString, "Specify which camera to operate on", "camera", ArgumentRequired, "camera"); + parser.addOption(OptCapture, OptionNone, + "Capture until interrupted by user", "capture"); parser.addOption(OptFormat, &formatKeyValue, "Set format of the camera's first stream", "format"); parser.addOption(OptHelp, OptionNone, "Display this help message", @@ -66,30 +70,25 @@ static int parseOptions(int argc, char *argv[]) return 0; } -bool configureStreams(Camera *camera, std::vector &streams) +static bool configureStreams(Camera *camera, std::vector &streams) { KeyValueParser::Options format = options[OptFormat]; - - if (streams.size() != 1) { - std::cout << "Camera has " << streams.size() - << " streams, I only know how to work with 1" - << std::endl; - return false; - } Stream *id = streams.front(); std::map config = camera->streamConfiguration(streams); - if (format.isSet("width")) - config[id].width = format["width"]; + if (options.isSet(OptFormat)) { + if (format.isSet("width")) + config[id].width = format["width"]; - if (format.isSet("height")) - config[id].height = format["height"]; + if (format.isSet("height")) + config[id].height = format["height"]; - /* TODO: Translate 4CC string to ID. */ - if (format.isSet("pixelformat")) - config[id].pixelFormat = format["pixelformat"]; + /* TODO: Translate 4CC string to ID. */ + if (format.isSet("pixelformat")) + config[id].pixelFormat = format["pixelformat"]; + } if (camera->configureStreams(config)) return false; @@ -97,6 +96,89 @@ bool configureStreams(Camera *camera, std::vector &streams) return true; } +static void requestComplete(Request *request, const std::map &buffers) +{ + static uint64_t last = 0; + + Buffer *buffer = buffers.begin()->second; + + double fps = buffer->timestamp() - last; + fps = last && fps ? 1000000000.0 / fps : 0.0; + last = buffer->timestamp(); + + std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence() + << " buf: " << buffer->index() + << " bytesused: " << buffer->bytesused() + << " timestamp: " << buffer->timestamp() + << " fps: " << std::fixed << std::setprecision(2) << fps + << std::endl; + + request = camera->createRequest(); + if (!request) { + std::cerr << "Can't create request" << std::endl; + return; + } + + request->setBuffers(buffers); + camera->queueRequest(request); +} + +static int capture() +{ + int ret; + + std::vector streams = camera->streams(); + + ret = configureStreams(camera.get(), streams); + if (ret < 0) { + std::cout << "Failed to configure camera" << std::endl; + return ret; + } + + Stream *stream = streams.front(); + + ret = camera->allocateBuffers(); + if (ret) { + std::cerr << "Failed to allocate buffers" + << std::endl; + return ret; + } + + camera->requestCompleted.connect(requestComplete); + + BufferPool &pool = stream->bufferPool(); + + for (Buffer &buffer : pool.buffers()) { + Request *request = camera->createRequest(); + if (!request) { + std::cerr << "Can't create request" << std::endl; + return -ENOMEM; + } + + std::map map; + map[stream] = &buffer; + ret = request->setBuffers(map); + if (ret < 0) { + std::cerr << "Can't set buffers for request" << std::endl; + return ret; + } + + ret = camera->queueRequest(request); + if (ret < 0) { + std::cerr << "Can't queue request" << std::endl; + return ret; + } + } + + std::cout << "Capture until user interrupts by SIGINT" << std::endl; + camera->start(); + + ret = loop->exec(); + + camera->stop(); + return ret; +} + int main(int argc, char **argv) { int ret; @@ -106,8 +188,6 @@ int main(int argc, char **argv) return EXIT_FAILURE; CameraManager *cm = CameraManager::instance(); - std::shared_ptr camera; - std::vector streams; ret = cm->start(); if (ret) { @@ -136,7 +216,13 @@ int main(int argc, char **argv) goto out; } - streams = camera->streams(); + const std::vector &streams = camera->streams(); + if (streams.size() != 1) { + std::cout << "Camera has " << streams.size() + << " streams, only 1 is supported" + << std::endl; + goto out; + } if (camera->acquire()) { std::cout << "Failed to acquire camera" << std::endl; @@ -146,22 +232,17 @@ int main(int argc, char **argv) std::cout << "Using camera " << camera->name() << std::endl; } - if (options.isSet(OptFormat)) { + if (options.isSet(OptCapture)) { if (!camera) { - std::cout << "Can't configure stream, no camera selected" + std::cout << "Can't capture without a camera" << std::endl; - goto out_camera; + ret = EXIT_FAILURE; + goto out; } - if (!configureStreams(camera.get(), streams)) { - std::cout << "Failed to configure camera" << std::endl; - goto out_camera; - } + capture(); } - ret = loop->exec(); - -out_camera: if (camera) { camera->release(); camera.reset(); From patchwork Wed Feb 6 06:08:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 541 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 C3BAE6104C for ; Wed, 6 Feb 2019 07:08:29 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7146C2D7 for ; Wed, 6 Feb 2019 07:08:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433309; bh=YV6PONACtOw0/26YMEsBf0W+VyGYT+We1vhnqpPw4Ag=; h=From:To:Subject:Date:In-Reply-To:References:From; b=cyA8wSHf1/mISH5pFB/O9Q6ARYR5YWzh/WjtQlVLQxLo0aQkjTZju1QC6+uK5L2XF pzyQyKz22yajNQGjj4h2yPUGan9fZqs1ft/X+iWXB8hq/jDMhTDuaS0qdeHuBSC0TA 1mES401DvDhCwsAofjHEvcLRfoSVqqZbSdrESWVM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:17 +0200 Message-Id: <20190206060818.13907-27-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 26/27] cam: Add BufferWriter helper 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:30 -0000 From: Niklas Söderlund Add a simpler helper to allow the cam application to write raw captured frames to disk. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- src/cam/buffer_writer.cpp | 63 +++++++++++++++++++++++++++++++++++++++ src/cam/buffer_writer.h | 25 ++++++++++++++++ src/cam/meson.build | 1 + 3 files changed, 89 insertions(+) create mode 100644 src/cam/buffer_writer.cpp create mode 100644 src/cam/buffer_writer.h diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp new file mode 100644 index 000000000000..2d2258b4cd1c --- /dev/null +++ b/src/cam/buffer_writer.cpp @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * buffer_writer.cpp - Buffer writer + */ + +#include +#include +#include +#include +#include +#include + +#include "buffer_writer.h" + +BufferWriter::BufferWriter(const std::string &pattern) + : pattern_(pattern) +{ +} + +int BufferWriter::write(libcamera::Buffer *buffer) +{ + std::string filename; + size_t pos; + int fd, ret = 0; + + filename = pattern_; + pos = filename.find_first_of('#'); + if (pos != std::string::npos) { + std::stringstream ss; + ss << std::setw(6) << std::setfill('0') << buffer->sequence(); + filename.replace(pos, 1, ss.str()); + } + + fd = open(filename.c_str(), O_CREAT | O_WRONLY | + (pos == std::string::npos ? O_APPEND : O_TRUNC), + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd == -1) + return -errno; + + for (libcamera::Plane &plane : buffer->planes()) { + void *data = plane.mem(); + unsigned int length = plane.length(); + + ret = ::write(fd, data, length); + if (ret < 0) { + ret = -errno; + std::cerr << "write error: " << strerror(-ret) + << std::endl; + break; + } else if (ret != (int)length) { + std::cerr << "write error: only " << ret + << " bytes written instead of " + << length << std::endl; + break; + } + } + + close(fd); + + return ret; +} diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h new file mode 100644 index 000000000000..9705773e0e39 --- /dev/null +++ b/src/cam/buffer_writer.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * buffer_writer.h - Buffer writer + */ +#ifndef __LIBCAMERA_BUFFER_WRITER_H__ +#define __LIBCAMERA_BUFFER_WRITER_H__ + +#include + +#include + +class BufferWriter +{ +public: + BufferWriter(const std::string &pattern = "frame-#.bin"); + + int write(libcamera::Buffer *buffer); + +private: + std::string pattern_; +}; + +#endif /* __LIBCAMERA_BUFFER_WRITER_H__ */ diff --git a/src/cam/meson.build b/src/cam/meson.build index cc647523955f..1a1244fe25cd 100644 --- a/src/cam/meson.build +++ b/src/cam/meson.build @@ -1,4 +1,5 @@ cam_sources = files([ + 'buffer_writer.cpp', 'event_loop.cpp', 'main.cpp', 'options.cpp', From patchwork Wed Feb 6 06:08:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 542 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 0A27461047 for ; Wed, 6 Feb 2019 07:08:30 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A999C41 for ; Wed, 6 Feb 2019 07:08:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433309; bh=NB6qFUY2NGlMoTYBWRvhf3ofIxPgq9TkA0jR21AHXBs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=aLvBMQ67/j+1JkTRkJ6SJw28HDgqW4vVeCiSK9EtNc83WxRE8mkadxf2CFlWKDprq zEAKbE/F8N/VhezQcMnBXU09HhCOkrQPvsQNH/Y5slbrBAFfXlCwYzwFCuMEyVCYWm iYrNc4s3yTprv9Ha7t2f4FJWAC1tF4VZNOUfyIfg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:08:18 +0200 Message-Id: <20190206060818.13907-28-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 27/27] cam: Add option to write raw frames to disk 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:30 -0000 From: Niklas Söderlund Use the helper BufferWriter to optionally write frames to disk as they are captured. Signed-off-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- src/cam/main.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/cam/main.cpp b/src/cam/main.cpp index ec5040e19935..9b67ab75a6a1 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -13,6 +13,7 @@ #include +#include "buffer_writer.h" #include "event_loop.h" #include "options.h" @@ -21,10 +22,12 @@ using namespace libcamera; OptionsParser::Options options; std::shared_ptr camera; EventLoop *loop; +BufferWriter *writer; enum { OptCamera = 'c', OptCapture = 'C', + OptFile = 'F', OptFormat = 'f', OptHelp = 'h', OptList = 'l', @@ -52,6 +55,11 @@ static int parseOptions(int argc, char *argv[]) ArgumentRequired, "camera"); parser.addOption(OptCapture, OptionNone, "Capture until interrupted by user", "capture"); + parser.addOption(OptFile, OptionString, + "Write captured frames to disk\n" + "The first '#' character in the file name is expanded to the frame sequence number.\n" + "The default file name is 'frame-#.bin'.", + "file", ArgumentOptional, "filename"); parser.addOption(OptFormat, &formatKeyValue, "Set format of the camera's first stream", "format"); parser.addOption(OptHelp, OptionNone, "Display this help message", @@ -113,6 +121,9 @@ static void requestComplete(Request *request, const std::map << " fps: " << std::fixed << std::setprecision(2) << fps << std::endl; + if (writer) + writer->write(buffer); + request = camera->createRequest(); if (!request) { std::cerr << "Can't create request" << std::endl; @@ -240,7 +251,19 @@ int main(int argc, char **argv) goto out; } + if (options.isSet(OptFile)) { + if (!options[OptFile].toString().empty()) + writer = new BufferWriter(options[OptFile]); + else + writer = new BufferWriter(); + } + capture(); + + if (options.isSet(OptFile)) { + delete writer; + writer = nullptr; + } } if (camera) {