From patchwork Sun Jan 12 01:01:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2583 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 001726067C for ; Sun, 12 Jan 2020 02:02:58 +0100 (CET) X-Halon-ID: 43fcbf79-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 43fcbf79-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:02:55 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:41 +0100 Message-Id: <20200112010212.2609025-2-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 01/32] libcamera: request: remove prepare() X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:02:59 -0000 The association of buffers to a request can be done directly in addBuffer() instead of when the request is queued to the camera. Keep the check that a request contains buffers by moving it to Camera::queueRequest() where prepare() was previously called. As a bonus we can remove a friend statement in Request. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- include/libcamera/request.h | 2 -- src/libcamera/buffer.cpp | 4 ++-- src/libcamera/camera.cpp | 11 +++++------ src/libcamera/request.cpp | 26 ++------------------------ 4 files changed, 9 insertions(+), 34 deletions(-) diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 2d5a5964e99eb75f..728f380de4f010c3 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -48,10 +48,8 @@ public: bool hasPendingBuffers() const { return !pending_.empty(); } private: - friend class Camera; friend class PipelineHandler; - int prepare(); void complete(); bool completeBuffer(Buffer *buffer); diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index 960eeb8f7d193ddd..5305e3c391da6a69 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -360,7 +360,7 @@ Buffer::Buffer(unsigned int index, const Buffer *metadata) * The intended callers of this method are buffer completion handlers that * need to associate a buffer to the request it belongs to. * - * A Buffer is associated to a request by Request::prepare() and the + * A Buffer is associated to a request by Request::addBuffer() and the * association is valid until the buffer completes. The returned request * pointer is valid only during that interval. * @@ -397,7 +397,7 @@ void Buffer::cancel() * \fn Buffer::setRequest() * \brief Set the request this buffer belongs to * - * The intended callers are Request::prepare() and Request::completeBuffer(). + * The intended callers are Request::addBuffer() and Request::completeBuffer(). */ } /* namespace libcamera */ diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index e810fb725d81350d..5d294b10647db8da 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -810,6 +810,11 @@ int Camera::queueRequest(Request *request) if (!stateIs(CameraRunning)) return -EACCES; + if (request->buffers().empty()) { + LOG(Camera, Error) << "Request contains no buffers"; + return -EINVAL; + } + for (auto const &it : request->buffers()) { Stream *stream = it.first; Buffer *buffer = it.second; @@ -832,12 +837,6 @@ int Camera::queueRequest(Request *request) buffer->mem_ = &stream->buffers()[buffer->index_]; } - int ret = request->prepare(); - if (ret) { - LOG(Camera, Error) << "Failed to prepare request"; - return ret; - } - return pipe_->queueRequest(this, request); } diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index c14ed1a4d3ce55d0..84a5f55879ccf5b5 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -139,6 +139,8 @@ int Request::addBuffer(std::unique_ptr buffer) return -EEXIST; } + buffer->setRequest(this); + pending_.insert(buffer.get()); bufferMap_[stream] = buffer.release(); return 0; @@ -203,30 +205,6 @@ Buffer *Request::findBuffer(Stream *stream) const * otherwise */ -/** - * \brief Validate the request and prepare it for the completion handler - * - * Requests that contain no buffers are invalid and are rejected. - * - * \return 0 on success or a negative error code otherwise - * \retval -EINVAL The request is invalid - */ -int Request::prepare() -{ - if (bufferMap_.empty()) { - LOG(Request, Error) << "Invalid request due to missing buffers"; - return -EINVAL; - } - - for (auto const &pair : bufferMap_) { - Buffer *buffer = pair.second; - buffer->setRequest(this); - pending_.insert(buffer); - } - - return 0; -} - /** * \brief Complete a queued request * From patchwork Sun Jan 12 01:01:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2584 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 29E31606CE for ; Sun, 12 Jan 2020 02:03:00 +0100 (CET) X-Halon-ID: 445f74e9-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 445f74e9-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:02:56 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:42 +0100 Message-Id: <20200112010212.2609025-3-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 02/32] libcamera: Add FileDescriptor to help pass numerical fds around X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:00 -0000 Add a helper to make it easier to pass file descriptors around. The helper class duplicates the fd which decouples it from the original fd which could be closed by its owner while the new FileDescriptor remains valid. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v3 - Add FileDescriptor::isValid() - Add logic in FileDescriptor::FileDescriptor(int fd) to reset the internal Descriptor in case the ::dup() should fail and the Fatal error do not abort(). --- include/libcamera/file_descriptor.h | 47 +++++++ include/libcamera/meson.build | 1 + src/libcamera/file_descriptor.cpp | 203 ++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 4 files changed, 252 insertions(+) create mode 100644 include/libcamera/file_descriptor.h create mode 100644 src/libcamera/file_descriptor.cpp diff --git a/include/libcamera/file_descriptor.h b/include/libcamera/file_descriptor.h new file mode 100644 index 0000000000000000..8612f86511a101ca --- /dev/null +++ b/include/libcamera/file_descriptor.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * file_descriptor.h - File descriptor wrapper + */ +#ifndef __LIBCAMERA_FILE_DESCRIPTOR_H__ +#define __LIBCAMERA_FILE_DESCRIPTOR_H__ + +#include + +namespace libcamera { + +class FileDescriptor final +{ +public: + explicit FileDescriptor(int fd = -1); + FileDescriptor(const FileDescriptor &other); + FileDescriptor(FileDescriptor &&other); + ~FileDescriptor(); + + FileDescriptor &operator=(const FileDescriptor &other); + FileDescriptor &operator=(FileDescriptor &&other); + + bool isValid() const { return fd_ != nullptr; } + int fd() const { return fd_ ? fd_->fd() : -1; } + FileDescriptor dup() const; + +private: + class Descriptor + { + public: + Descriptor(int fd); + ~Descriptor(); + + int fd() const { return fd_; } + + private: + int fd_; + }; + + std::shared_ptr fd_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_FILE_DESCRIPTOR_H__ */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 99abf06099407c1f..543e6773cc5158a0 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -6,6 +6,7 @@ libcamera_api = files([ 'controls.h', 'event_dispatcher.h', 'event_notifier.h', + 'file_descriptor.h', 'geometry.h', 'logging.h', 'object.h', diff --git a/src/libcamera/file_descriptor.cpp b/src/libcamera/file_descriptor.cpp new file mode 100644 index 0000000000000000..88385476d8902be5 --- /dev/null +++ b/src/libcamera/file_descriptor.cpp @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * file_descriptor.cpp - File descriptor wrapper + */ + +#include + +#include +#include +#include + +#include "log.h" + +/** + * \file file_descriptor.h + * \brief File descriptor wrapper + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(FileDescriptor) + +/** + * \class FileDescriptor + * \brief RAII-style wrapper for file descriptors + * + * The FileDescriptor class provides RAII-style lifetime management of file + * descriptors with an efficient mechanism for ownership sharing. At its core, + * an internal Descriptor object wraps a file descriptor (expressed as a signed + * integer) with an RAII-style interface. The Descriptor is then implicitly + * shared with all FileDescriptor instances constructed as copies. + * + * When constructed from a numerical file descriptor, the FileDescriptor + * instance duplicates the file descriptor and wraps the duplicate as a + * Descriptor. The copy constructor and assignment operator create copies that + * share the Descriptor, while the move versions of those methods additionally + * make the other FileDescriptor invalid. When the last FileDescriptor that + * references a Descriptor is destroyed, the file descriptor is closed. + * + * The numerical file descriptor is available through the fd() method. As + * constructing a FileDescriptor from a numerical file descriptor duplicates + * the file descriptor, the value returned by fd() will be different than the + * value passed to the constructor. All FileDescriptor instances created as + * copies of a FileDescriptor will report the same fd() value. Callers can + * perform operations on the fd(), but shall never close it manually. + */ + +/** + * \brief Create a FileDescriptor wrapping a copy of a given \a fd + * \param[in] fd File descriptor + * + * Constructing a FileDescriptor from a numerical file descriptor duplicates the + * \a fd and takes ownership of the copy. The original \a fd is left untouched, + * and the caller is responsible for closing it when appropriate. The duplicated + * file descriptor will be closed automatically when all FileDescriptor + * instances that reference it are destroyed. + * + * If the \a fd is negative, the FileDescriptor is constructed as invalid and + * the fd() method will return -1. + */ +FileDescriptor::FileDescriptor(int fd) +{ + if (fd < 0) + return; + + fd_ = std::make_shared(fd); + if (fd_->fd() < 0) + fd_.reset(); +} + +/** + * \brief Copy constructor, create a FileDescriptor from a copy of \a other + * \param[in] other The other FileDescriptor + * + * Copying a FileDescriptor implicitly shares ownership of the wrapped file + * descriptor. The original FileDescriptor is left untouched, and the caller is + * responsible for destroying it when appropriate. The wrapped file descriptor + * will be closed automatically when all FileDescriptor instances that + * reference it are destroyed. + */ +FileDescriptor::FileDescriptor(const FileDescriptor &other) + : fd_(other.fd_) +{ +} + +/** + * \brief Move constructor, create a FileDescriptor by taking over \a other + * \param[in] other The other FileDescriptor + * + * Moving a FileDescriptor moves the reference to the wrapped descriptor owned + * by \a other to the new FileDescriptor. The \a other FileDescriptor is + * invalidated and its fd() method will return -1. The wrapped file descriptor + * will be closed automatically when all FileDescriptor instances that + * reference it are destroyed. + */ +FileDescriptor::FileDescriptor(FileDescriptor &&other) + : fd_(std::move(other.fd_)) +{ +} + +/** + * \brief Destroy the FileDescriptor instance + * + * Destroying a FileDescriptor instance releases its reference to the wrapped + * descriptor, if any. When the last instance that references a wrapped + * descriptor is destroyed, the file descriptor is automatically closed. + */ +FileDescriptor::~FileDescriptor() +{ +} + +/** + * \brief Copy assignment operator, replace the wrapped file descriptor with a + * copy of \a other + * \param[in] other The other FileDescriptor + * + * Copying a FileDescriptor creates a new reference to the wrapped file + * descriptor owner by \a other. If \a other is invalid, *this will also be + * invalid. The original FileDescriptor is left untouched, and the caller is + * responsible for destroying it when appropriate. The wrapped file descriptor + * will be closed automatically when all FileDescriptor instances that + * reference it are destroyed. + * + * \return A reference to this FileDescriptor + */ +FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other) +{ + fd_ = other.fd_; + + return *this; +} + +/** + * \brief Move assignment operator, replace the wrapped file descriptor by + * taking over \a other + * \param[in] other The other FileDescriptor + * + * Moving a FileDescriptor moves the reference to the wrapped descriptor owned + * by \a other to the new FileDescriptor. If \a other is invalid, *this will + * also be invalid. The \a other FileDescriptor is invalidated and its fd() + * method will return -1. The wrapped file descriptor will be closed + * automatically when all FileDescriptor instances that reference it are + * destroyed. + * + * \return A reference to this FileDescriptor + */ +FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other) +{ + fd_ = std::move(other.fd_); + + return *this; +} + +/** + * \fn FileDescriptor::isValid() + * \brief Check if the FileDescriptor instance is valid + * \return True if the FileDescriptor is valid, false otherwise + */ + +/** + * \fn FileDescriptor::fd() + * \brief Retrieve the numerical file descriptor + * \return The numerical file descriptor, which may be -1 if the FileDescriptor + * instance is invalid + */ + +/** + * \brief Duplicate a FileDescriptor + * + * Duplicating a FileDescriptor creates a duplicate of the wrapped file + * descriptor and returns a new FileDescriptor instance that wraps the + * duplicate. The fd() method of the original and duplicate instances will + * return different values. The duplicate instance will not be affected by + * destruction of the original instance or its copies. + * + * \return A new FileDescriptor instance wrapping a duplicate of the original + * file descriptor + */ +FileDescriptor FileDescriptor::dup() const +{ + return FileDescriptor(fd()); +} + +FileDescriptor::Descriptor::Descriptor(int fd) +{ + /* Failing to dup() a fd should not happen and is fatal. */ + fd_ = ::dup(fd); + if (fd_ == -1) { + int ret = -errno; + LOG(FileDescriptor, Fatal) + << "Failed to dup() fd: " << strerror(-ret); + } +} + +FileDescriptor::Descriptor::~Descriptor() +{ + if (fd_ != -1) + close(fd_); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index c4f965bd7413b37e..722c5bc15afe52ef 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -14,6 +14,7 @@ libcamera_sources = files([ 'event_dispatcher.cpp', 'event_dispatcher_poll.cpp', 'event_notifier.cpp', + 'file_descriptor.cpp', 'formats.cpp', 'geometry.cpp', 'ipa_context_wrapper.cpp', From patchwork Sun Jan 12 01:01:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2585 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D65DD606A2 for ; Sun, 12 Jan 2020 02:03:00 +0100 (CET) X-Halon-ID: 450203ae-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 450203ae-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:02:57 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:43 +0100 Message-Id: <20200112010212.2609025-4-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 03/32] test: file_descriptor: Add test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:01 -0000 Add a test which exercises the whole FileDescriptor interface. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- test/file-descriptor.cpp | 208 +++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 2 files changed, 209 insertions(+) create mode 100644 test/file-descriptor.cpp diff --git a/test/file-descriptor.cpp b/test/file-descriptor.cpp new file mode 100644 index 0000000000000000..3e5e880093e748b0 --- /dev/null +++ b/test/file-descriptor.cpp @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * file_descriptor.cpp - FileDescriptor test + */ + +#include +#include +#include +#include +#include + +#include + +#include "test.h" + +using namespace libcamera; +using namespace std; + +class FileDescriptorTest : public Test +{ +protected: + int init() + { + desc1_ = nullptr; + desc2_ = nullptr; + + fd_ = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); + if (fd_ < 0) + return TestFail; + + /* Cache inode number of temp file. */ + struct stat s; + if (fstat(fd_, &s)) + return TestFail; + + inodeNr_ = s.st_ino; + + return 0; + } + + int run() + { + /* Test creating empty FileDescriptor. */ + desc1_ = new FileDescriptor(); + + if (desc1_->fd() != -1) { + std::cout << "Failed fd numerical check (default constructor)" + << std::endl; + return TestFail; + } + + delete desc1_; + desc1_ = nullptr; + + /* Test creating FileDescriptor from numerical file descriptor. */ + desc1_ = new FileDescriptor(fd_); + if (desc1_->fd() == fd_) { + std::cout << "Failed fd numerical check (int constructor)" + << std::endl; + return TestFail; + } + + if (!isValidFd(fd_) || !isValidFd(desc1_->fd())) { + std::cout << "Failed fd validity after construction (int constructor)" + << std::endl; + return TestFail; + } + + int fd = desc1_->fd(); + + delete desc1_; + desc1_ = nullptr; + + if (!isValidFd(fd_) || isValidFd(fd)) { + std::cout << "Failed fd validity after destruction (int constructor)" + << std::endl; + return TestFail; + } + + /* Test creating FileDescriptor from other FileDescriptor. */ + desc1_ = new FileDescriptor(fd_); + desc2_ = new FileDescriptor(*desc1_); + + if (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) { + std::cout << "Failed fd numerical check (copy constructor)" + << std::endl; + return TestFail; + } + + if (!isValidFd(desc1_->fd()) || !isValidFd(desc2_->fd())) { + std::cout << "Failed fd validity after construction (copy constructor)" + << std::endl; + return TestFail; + } + + delete desc1_; + desc1_ = nullptr; + + if (!isValidFd(desc2_->fd())) { + std::cout << "Failed fd validity after destruction (copy constructor)" + << std::endl; + return TestFail; + } + + delete desc2_; + desc2_ = nullptr; + + /* Test creating FileDescriptor by taking over other FileDescriptor. */ + desc1_ = new FileDescriptor(fd_); + fd = desc1_->fd(); + desc2_ = new FileDescriptor(std::move(*desc1_)); + + if (desc1_->fd() != -1 || desc2_->fd() != fd) { + std::cout << "Failed fd numerical check (move constructor)" + << std::endl; + return TestFail; + } + + if (!isValidFd(desc2_->fd())) { + std::cout << "Failed fd validity after construction (move constructor)" + << std::endl; + return TestFail; + } + + delete desc1_; + desc1_ = nullptr; + delete desc2_; + desc2_ = nullptr; + + /* Test creating FileDescriptor by copy assignment. */ + desc1_ = new FileDescriptor(); + desc2_ = new FileDescriptor(fd_); + + fd = desc2_->fd(); + *desc1_ = *desc2_; + + if (desc1_->fd() != fd || desc2_->fd() != fd) { + std::cout << "Failed fd numerical check (copy assignment)" + << std::endl; + return TestFail; + } + + if (!isValidFd(desc1_->fd()) || !isValidFd(desc2_->fd())) { + std::cout << "Failed fd validity after construction (copy assignment)" + << std::endl; + return TestFail; + } + + delete desc1_; + desc1_ = nullptr; + delete desc2_; + desc2_ = nullptr; + + /* Test creating FileDescriptor by move assignment. */ + desc1_ = new FileDescriptor(); + desc2_ = new FileDescriptor(fd_); + + fd = desc2_->fd(); + *desc1_ = std::move(*desc2_); + + if (desc1_->fd() != fd || desc2_->fd() != -1) { + std::cout << "Failed fd numerical check (move assignment)" + << std::endl; + return TestFail; + } + + if (!isValidFd(desc1_->fd())) { + std::cout << "Failed fd validity after construction (move assignment)" + << std::endl; + return TestFail; + } + + delete desc1_; + desc1_ = nullptr; + delete desc2_; + desc2_ = nullptr; + + return TestPass; + } + + void cleanup() + { + delete desc2_; + delete desc1_; + + if (fd_ > 0) + close(fd_); + } + +private: + bool isValidFd(int fd) + { + struct stat s; + if (fstat(fd, &s)) + return false; + + /* Check that inode number matches cached temp file. */ + return s.st_ino == inodeNr_; + } + + int fd_; + ino_t inodeNr_; + FileDescriptor *desc1_, *desc2_; +}; + +TEST_REGISTER(FileDescriptorTest) diff --git a/test/meson.build b/test/meson.build index 1bb2161dc05a7b1d..daaa1aac926dd0ea 100644 --- a/test/meson.build +++ b/test/meson.build @@ -25,6 +25,7 @@ internal_tests = [ ['event', 'event.cpp'], ['event-dispatcher', 'event-dispatcher.cpp'], ['event-thread', 'event-thread.cpp'], + ['file-descriptor', 'file-descriptor.cpp'], ['message', 'message.cpp'], ['object', 'object.cpp'], ['object-invoke', 'object-invoke.cpp'], From patchwork Sun Jan 12 01:01:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2586 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BEAF6606CE for ; Sun, 12 Jan 2020 02:03:01 +0100 (CET) X-Halon-ID: 457c5258-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 457c5258-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:02:57 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:44 +0100 Message-Id: <20200112010212.2609025-5-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 04/32] v4l2: Rename FrameMetadata to V4L2FrameMetadata X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:02 -0000 With the upcoming FrameBuffer API a new library wide FrameMetadata object will be added which will replace the specific implementation in the V4L2 compatibility layer. Avoid name collisions while the new FrameBuffer API is added by renaming the V4L2 compatibility layer specific implementation until it can be replaced with the library wide implementation. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/v4l2/v4l2_camera.cpp | 12 ++++++------ src/v4l2/v4l2_camera.h | 8 ++++---- src/v4l2/v4l2_camera_proxy.cpp | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 4545483cf1c7373c..2a507b9bb8318025 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -16,7 +16,7 @@ using namespace libcamera; LOG_DECLARE_CATEGORY(V4L2Compat); -FrameMetadata::FrameMetadata(Buffer *buffer) +V4L2FrameMetadata::V4L2FrameMetadata(Buffer *buffer) : index_(buffer->index()), bytesused_(buffer->bytesused()), timestamp_(buffer->timestamp()), sequence_(buffer->sequence()), status_(buffer->status()) @@ -61,12 +61,12 @@ void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig) *streamConfig = config_->at(0); } -std::vector V4L2Camera::completedBuffers() +std::vector V4L2Camera::completedBuffers() { - std::vector v; + std::vector v; bufferLock_.lock(); - for (std::unique_ptr &metadata : completedBuffers_) + for (std::unique_ptr &metadata : completedBuffers_) v.push_back(*metadata.get()); completedBuffers_.clear(); bufferLock_.unlock(); @@ -82,8 +82,8 @@ void V4L2Camera::requestComplete(Request *request) /* We only have one stream at the moment. */ bufferLock_.lock(); Buffer *buffer = request->buffers().begin()->second; - std::unique_ptr metadata = - utils::make_unique(buffer); + std::unique_ptr metadata = + utils::make_unique(buffer); completedBuffers_.push_back(std::move(metadata)); bufferLock_.unlock(); diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h index 5a889efdb4a23bd2..81f7908e5e8a6beb 100644 --- a/src/v4l2/v4l2_camera.h +++ b/src/v4l2/v4l2_camera.h @@ -19,10 +19,10 @@ using namespace libcamera; -class FrameMetadata +class V4L2FrameMetadata { public: - FrameMetadata(Buffer *buffer); + V4L2FrameMetadata(Buffer *buffer); int index() const { return index_; } @@ -51,7 +51,7 @@ public: int open(); void close(); void getStreamConfig(StreamConfiguration *streamConfig); - std::vector completedBuffers(); + std::vector completedBuffers(); void *mmap(unsigned int index); @@ -79,7 +79,7 @@ private: std::mutex bufferLock_; std::deque> pendingRequests_; - std::deque> completedBuffers_; + std::deque> completedBuffers_; }; #endif /* __V4L2_CAMERA_H__ */ diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp index 00d0d57172022138..52f8468cdaa06a7a 100644 --- a/src/v4l2/v4l2_camera_proxy.cpp +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -174,8 +174,8 @@ void V4L2CameraProxy::querycap(std::shared_ptr camera) void V4L2CameraProxy::updateBuffers() { - std::vector completedBuffers = vcam_->completedBuffers(); - for (FrameMetadata &fmd : completedBuffers) { + std::vector completedBuffers = vcam_->completedBuffers(); + for (V4L2FrameMetadata &fmd : completedBuffers) { struct v4l2_buffer &buf = buffers_[fmd.index()]; switch (fmd.status()) { From patchwork Sun Jan 12 01:01:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2587 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 73D1C606D0 for ; Sun, 12 Jan 2020 02:03:02 +0100 (CET) X-Halon-ID: 45f62063-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 45f62063-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:02:58 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:45 +0100 Message-Id: <20200112010212.2609025-6-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 05/32] v4l2: camera: Handle memory mapping of buffers directly X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:02 -0000 In the upcoming FrameBuffer API the memory mapping of buffers will be left to the user of the FrameBuffer objects. Prepare the V4L2 compatibility layer to this upcoming change to ease conversion to the new API. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v3 - Return FileDescriptor from getBufferFd() - Call fops().munmap() in V4L2CameraProxy::munmap() --- src/v4l2/v4l2_camera.cpp | 12 ++++++------ src/v4l2/v4l2_camera.h | 5 +++-- src/v4l2/v4l2_camera_proxy.cpp | 24 +++++++++++++++++++----- src/v4l2/v4l2_camera_proxy.h | 2 +- src/v4l2/v4l2_compat_manager.cpp | 2 +- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 2a507b9bb8318025..07f05a31261c1b25 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -121,12 +121,6 @@ int V4L2Camera::configure(StreamConfiguration *streamConfigOut, return 0; } -void *V4L2Camera::mmap(unsigned int index) -{ - Stream *stream = *camera_->streams().begin(); - return stream->buffers()[index].planes()[0].mem(); -} - int V4L2Camera::allocBuffers(unsigned int count) { int ret = camera_->allocateBuffers(); @@ -138,6 +132,12 @@ void V4L2Camera::freeBuffers() camera_->freeBuffers(); } +FileDescriptor V4L2Camera::getBufferFd(unsigned int index) +{ + Stream *stream = *camera_->streams().begin(); + return FileDescriptor(stream->buffers()[index].planes()[0].dmabuf()); +} + int V4L2Camera::streamOn() { if (isRunning_) diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h index 81f7908e5e8a6beb..f760316c854fba2f 100644 --- a/src/v4l2/v4l2_camera.h +++ b/src/v4l2/v4l2_camera.h @@ -14,6 +14,7 @@ #include #include +#include #include "semaphore.h" @@ -53,14 +54,14 @@ public: void getStreamConfig(StreamConfiguration *streamConfig); std::vector completedBuffers(); - void *mmap(unsigned int index); - int configure(StreamConfiguration *streamConfigOut, const Size &size, PixelFormat pixelformat, unsigned int bufferCount); int allocBuffers(unsigned int count); void freeBuffers(); + FileDescriptor getBufferFd(unsigned int index); + int streamOn(); int streamOff(); diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp index 52f8468cdaa06a7a..b4a9e2107c0f9f28 100644 --- a/src/v4l2/v4l2_camera_proxy.cpp +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -75,7 +75,8 @@ void V4L2CameraProxy::close() vcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking); } -void *V4L2CameraProxy::mmap(size_t length, int prot, int flags, off_t offset) +void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags, + off_t offset) { LOG(V4L2Compat, Debug) << "Servicing mmap"; @@ -91,13 +92,22 @@ void *V4L2CameraProxy::mmap(size_t length, int prot, int flags, off_t offset) return MAP_FAILED; } - void *val = vcam_->invokeMethod(&V4L2Camera::mmap, - ConnectionTypeBlocking, index); + FileDescriptor fd = vcam_->invokeMethod(&V4L2Camera::getBufferFd, + ConnectionTypeBlocking, index); + if (!fd.isValid()) { + errno = EINVAL; + return MAP_FAILED; + } + + void *map = V4L2CompatManager::instance()->fops().mmap(addr, length, prot, + flags, fd.fd(), 0); + if (map == MAP_FAILED) + return map; buffers_[index].flags |= V4L2_BUF_FLAG_MAPPED; - mmaps_[val] = index; + mmaps_[map] = index; - return val; + return map; } int V4L2CameraProxy::munmap(void *addr, size_t length) @@ -110,6 +120,10 @@ int V4L2CameraProxy::munmap(void *addr, size_t length) return -1; } + if (V4L2CompatManager::instance()->fops().munmap(addr, length)) + LOG(V4L2Compat, Error) << "Unmapping " << addr + << " with length " << length; + buffers_[iter->second].flags &= ~V4L2_BUF_FLAG_MAPPED; mmaps_.erase(iter); diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h index b59d19d707590d88..c8e61adf80f1b93b 100644 --- a/src/v4l2/v4l2_camera_proxy.h +++ b/src/v4l2/v4l2_camera_proxy.h @@ -28,7 +28,7 @@ public: int open(bool nonBlocking); void dup(); void close(); - void *mmap(size_t length, int prot, int flags, off_t offset); + void *mmap(void *addr, size_t length, int prot, int flags, off_t offset); int munmap(void *addr, size_t length); int ioctl(unsigned long request, void *arg); diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp index fe453445a9fb2cdd..f5a7b2ac4229d5d5 100644 --- a/src/v4l2/v4l2_compat_manager.cpp +++ b/src/v4l2/v4l2_compat_manager.cpp @@ -223,7 +223,7 @@ void *V4L2CompatManager::mmap(void *addr, size_t length, int prot, int flags, if (!proxy) return fops_.mmap(addr, length, prot, flags, fd, offset); - void *map = proxy->mmap(length, prot, flags, offset); + void *map = proxy->mmap(addr, length, prot, flags, offset); if (map == MAP_FAILED) return map; From patchwork Sun Jan 12 01:01:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2588 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 14530606D0 for ; Sun, 12 Jan 2020 02:03:03 +0100 (CET) X-Halon-ID: 466f071e-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 466f071e-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:02:59 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:46 +0100 Message-Id: <20200112010212.2609025-7-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 06/32] v4l2: camera_proxy: Call V4L2Camera::getBufferFd() directly X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:03 -0000 From: Laurent Pinchart The V4L2Camera::getBufferFd() method doesn't need to run in the camera thread. Call it directly. Signed-off-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/v4l2/v4l2_camera_proxy.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp index b4a9e2107c0f9f28..e4aba33e6d33f21b 100644 --- a/src/v4l2/v4l2_camera_proxy.cpp +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -92,8 +92,7 @@ void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags, return MAP_FAILED; } - FileDescriptor fd = vcam_->invokeMethod(&V4L2Camera::getBufferFd, - ConnectionTypeBlocking, index); + FileDescriptor fd = vcam_->getBufferFd(index); if (!fd.isValid()) { errno = EINVAL; return MAP_FAILED; From patchwork Sun Jan 12 01:01:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2589 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DC94A606E6 for ; Sun, 12 Jan 2020 02:03:03 +0100 (CET) X-Halon-ID: 46c8ceb6-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 46c8ceb6-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:00 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:47 +0100 Message-Id: <20200112010212.2609025-8-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 07/32] libcamera: buffer: Add FrameMetadata container for metadata information X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:04 -0000 With the introduction of FrameBuffer objects, the metadata information related to captured frames will not be stored directly in the frame buffer object. Add a new FrameMetadata class to hold this information. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- * Changes since v3 - Update comment for FrameMetadata::FrameCancelled * Changes since v2 - Clarify documentation for values in enum FrameMetadata::Status members. - Improved documentation. - Added todo to improve FrameMetadata::timestamp descripton. * Changes since v1: - Rename BufferInfo to FrameMetadata - Rename prefix for BufferInfo::Status s/Buffer/Frame/ - Make FrameMetadata a struct with public data members instead of a class with accessors methods. - Dropped FrameMetadata::update() - Documentation updates --- include/libcamera/buffer.h | 17 +++++++++ src/libcamera/buffer.cpp | 73 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index 80602124f24be4a0..0b95e41a2dd205b2 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -16,6 +16,23 @@ namespace libcamera { class Request; class Stream; +struct FrameMetadata { + enum Status { + FrameSuccess, + FrameError, + FrameCancelled, + }; + + struct Plane { + unsigned int bytesused; + }; + + Status status; + unsigned int sequence; + uint64_t timestamp; + std::vector planes; +}; + class Plane final { public: diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index 5305e3c391da6a69..360eb26cb4ca4906 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -23,6 +23,79 @@ namespace libcamera { LOG_DEFINE_CATEGORY(Buffer) +/** + * \struct FrameMetadata + * \brief Metadata related to a captured frame + * + * The FrameMetadata structure stores all metadata related to a captured frame, + * as stored in a FrameBuffer, such as capture status, timestamp and bytesused. + */ + +/** + * \enum FrameMetadata::Status + * \brief Define the frame completion status + * \var FrameMetadata::FrameSuccess + * The frame has been captured with success and contains valid data. All fields + * of the FrameMetadata structure are valid. + * \var FrameMetadata::FrameError + * An error occurred during capture of the frame. The frame data may be partly + * or fully invalid. The sequence and timestamp fields of the FrameMetadata + * structure is valid, the other fields may be invalid. + * \var FrameMetadata::FrameCancelled + * Capture stopped before the frame completed. The frame data is not valid. All + * fields of the FrameMetadata structure but the status field are invalid. + */ + +/** + * \struct FrameMetadata::Plane + * \brief Per-plane frame metadata + * + * Frames are stored in memory in one or multiple planes. The + * FrameMetadata::Plane structure stores per-plane metadata. + */ + +/** + * \var FrameMetadata::Plane::bytesused + * \brief Number of bytes occupied by the data in the plane, including line + * padding + * + * This value may vary per frame for compressed formats. For uncompressed + * formats it will be constant for all frames, but may be smaller than the + * FrameBuffer size. + */ + +/** + * \var FrameMetadata::status + * \brief Status of the frame + * + * The validity of other fields of the FrameMetadata structure depends on the + * status value. + */ + +/** + * \var FrameMetadata::sequence + * \brief Frame sequence number + * + * The sequence number is a monotonically increasing number assigned to the + * frames captured by the stream. The value is increased by one for each frame. + * Gaps in the sequence numbers indicate dropped frames. + */ + +/** + * \var FrameMetadata::timestamp + * \brief Time when the frame was captured + * + * The timestamp is expressed as a number of nanoseconds relative to the system + * clock since an unspecified time point. + * + * \todo Be more precise on what timestamps refer to. + */ + +/** + * \var FrameMetadata::planes + * \brief Array of per-plane metadata + */ + /** * \class Plane * \brief A memory region to store a single plane of a frame From patchwork Sun Jan 12 01:01:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2590 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B7463606D7 for ; Sun, 12 Jan 2020 02:03:04 +0100 (CET) X-Halon-ID: 474942cc-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 474942cc-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:00 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:48 +0100 Message-Id: <20200112010212.2609025-9-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 08/32] libcamera: buffer: Add FrameBuffer interface X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:05 -0000 Add a new FrameBuffer class to describe memory used to store frames. This change just adds the new interface, future patches will migrate all parts of libcamera to use this and replace the old Buffer interface. This change needs to specify the const keyword for Plane::length() as to not get Doxygen confused with FrameBuffer::Plane::length added in this change. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v3 - Delete moved-constructor and move-assignment operator - Update documentation. * Changes since v2 - Drop default value for planes argument for FrameBuffer::FrameBuffer(planes, cookie). - Deleted FrameBuffer copy constructor and assignment operator. - Add a Move constructor for FrameBuffer. - Update documentation. Changes since v1: - Rename FrameBuffer::info() to FrameBuffer::metadata() - Fixup commit message - Reorder method order - Rewrite documentation - Add operator==() and operator=!() for FrameBuffer::Plane --- include/libcamera/buffer.h | 38 ++++++++++++ src/libcamera/buffer.cpp | 117 ++++++++++++++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index 0b95e41a2dd205b2..e66e9c9cf828160a 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -11,6 +11,8 @@ #include #include +#include + namespace libcamera { class Request; @@ -33,6 +35,42 @@ struct FrameMetadata { std::vector planes; }; +class FrameBuffer final +{ +public: + struct Plane { + FileDescriptor fd; + unsigned int length; + }; + + FrameBuffer(const std::vector &planes, unsigned int cookie = 0); + + FrameBuffer(const FrameBuffer &) = delete; + FrameBuffer(FrameBuffer &&) = delete; + + FrameBuffer &operator=(const FrameBuffer &) = delete; + FrameBuffer &operator=(FrameBuffer &&) = delete; + + const std::vector &planes() const { return planes_; } + + Request *request() const { return request_; } + const FrameMetadata &metadata() const { return metadata_; }; + + unsigned int cookie() const { return cookie_; } + void setCookie(unsigned int cookie) { cookie_ = cookie; } + +private: + friend class Request; /* Needed to update request_. */ + friend class V4L2VideoDevice; /* Needed to update metadata_. */ + + std::vector planes_; + + Request *request_; + FrameMetadata metadata_; + + unsigned int cookie_; +}; + class Plane final { public: diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index 360eb26cb4ca4906..2178bd2fe17111dc 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -240,7 +240,7 @@ void *Plane::mem() } /** - * \fn Plane::length() + * \fn Plane::length() const * \brief Retrieve the length of the memory region * \return The length of the memory region */ @@ -473,4 +473,119 @@ void Buffer::cancel() * The intended callers are Request::addBuffer() and Request::completeBuffer(). */ +/** + * \class FrameBuffer + * \brief Frame buffer data and its associated dynamic metadata + * + * The FrameBuffer class is the primary interface for applications, IPAs and + * pipeline handlers to interact with frame memory. It contains all the static + * and dynamic information to manage the whole life cycle of a frame capture, + * from buffer creation to consumption. + * + * The static information describes the memory planes that make a frame. The + * planes are specified when creating the FrameBuffer and are expressed as a set + * of dmabuf file descriptors and length. + * + * The dynamic information is grouped in a FrameMetadata instance. It is updated + * during the processing of a queued capture request, and is valid from the + * completion of the buffer as signaled by Camera::bufferComplete() until the + * FrameBuffer is either reused in a new request or deleted. + * + * The creator of a FrameBuffer (application, IPA or pipeline handler) may + * associate to it an integer cookie for any private purpose. The cookie may be + * set when creating the FrameBuffer, and updated at any time with setCookie(). + * The cookie is transparent to the libcamera core and shall only be set by the + * creator of the FrameBuffer. This mechanism supplements the Request cookie. + */ + +/** + * \struct FrameBuffer::Plane + * \brief A memory region to store a single plane of a frame + * + * Planar pixel formats use multiple memory regions to store the different + * colour components of a frame. The Plane structure describes such a memory + * region by a dmabuf file descriptor and a length. A FrameBuffer then + * contains one or multiple planes, depending on the pixel format of the + * frames it is meant to store. + * + * To support DMA access, planes are associated with dmabuf objects represented + * by FileDescriptor handles. The Plane class doesn't handle mapping of the + * memory to the CPU, but applications and IPAs may use the dmabuf file + * descriptors to map the plane memory with mmap() and access its contents. + * + * \todo Once we have a Kernel API which can express offsets within a plane + * this structure shall be extended to contain this information. See commit + * 83148ce8be55e for initial documentation of this feature. + */ + +/** + * \var FrameBuffer::Plane::fd + * \brief The dmabuf file descriptor + */ + +/** + * \var FrameBuffer::Plane::length + * \brief The plane length in bytes + */ + +/** + * \brief Construct a FrameBuffer with an array of planes + * \param[in] planes The frame memory planes + * \param[in] cookie Cookie + */ +FrameBuffer::FrameBuffer(const std::vector &planes, unsigned int cookie) + : planes_(planes), request_(nullptr), cookie_(cookie) +{ +} + +/** + * \fn FrameBuffer::planes() + * \brief Retrieve the static plane descriptors + * \return Array of plane descriptors + */ + +/** + * \fn FrameBuffer::request() + * \brief Retrieve the request this buffer belongs to + * + * The intended callers of this method are buffer completion handlers that + * need to associate a buffer to the request it belongs to. + * + * A Buffer is associated to a request by Request::addBuffer() and the + * association is valid until the buffer completes. The returned request + * pointer is valid only during that interval. + * + * \return The Request the Buffer belongs to, or nullptr if the buffer is + * not associated with a request + */ + +/** + * \fn FrameBuffer::metadata() + * \brief Retrieve the dynamic metadata + * \return Dynamic metadata for the frame contained in the buffer + */ + +/** + * \fn FrameBuffer::cookie() + * \brief Retrieve the cookie + * + * The cookie belongs to the creator of the FrameBuffer, which controls its + * lifetime and value. + * + * \sa setCookie() + * + * \return The cookie + */ + +/** + * \fn FrameBuffer::setCookie() + * \brief Set the cookie + * \param[in] cookie Cookie to set + * + * The cookie belongs to the creator of the FrameBuffer. Its value may be + * modified at any time with this method. Applications and IPAs shall not modify + * the cookie value of buffers they haven't created themselves. The libcamera + * core never modifies the buffer cookie. + */ + } /* namespace libcamera */ From patchwork Sun Jan 12 01:01:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2591 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BD08B606DB for ; Sun, 12 Jan 2020 02:03:05 +0100 (CET) X-Halon-ID: 47c93fba-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 47c93fba-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:01 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:49 +0100 Message-Id: <20200112010212.2609025-10-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 09/32] ipa: Switch to FrameBuffer interface X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:06 -0000 Switch the IPA interfaces and implementations to use the Framebuffer interface. - The IPA interface is switched to use the simpler FrameBuffer::Plane container when carrying dmabuf descriptions (fd and length) over the pipeline/IPA boundary. - The RkISP1 IPA implementation takes advantage of the new simpler and safer (better control over file descriptors) FrameBuffer interface. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v2 - Update how FrameBuffers are handled in IPARkISP1::mapBuffers() and IPARkISP1::unmapBuffers() to match the changes to FrameBuffers. - Add todo about helper to mmap() in RkISP1 IPA. - Dropped left over paragraph in commit message from RFCv1 which should have been dropped in v1. --- include/ipa/ipa_interface.h | 2 +- src/ipa/libipa/ipa_interface_wrapper.cpp | 9 ++---- src/ipa/rkisp1/rkisp1.cpp | 40 +++++++++++++++++++---- src/libcamera/ipa_context_wrapper.cpp | 8 ++--- src/libcamera/ipa_interface.cpp | 7 ++-- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 12 +++++-- test/ipa/ipa_wrappers_test.cpp | 41 +++++++++++++----------- 7 files changed, 76 insertions(+), 43 deletions(-) diff --git a/include/ipa/ipa_interface.h b/include/ipa/ipa_interface.h index 0ea53d120fe10acf..229d1124b19ec1c9 100644 --- a/include/ipa/ipa_interface.h +++ b/include/ipa/ipa_interface.h @@ -105,7 +105,7 @@ struct IPAStream { struct IPABuffer { unsigned int id; - BufferMemory memory; + std::vector planes; }; struct IPAOperationData { diff --git a/src/ipa/libipa/ipa_interface_wrapper.cpp b/src/ipa/libipa/ipa_interface_wrapper.cpp index 6a389dfa714ab535..3628a785dc3e63f4 100644 --- a/src/ipa/libipa/ipa_interface_wrapper.cpp +++ b/src/ipa/libipa/ipa_interface_wrapper.cpp @@ -144,17 +144,14 @@ void IPAInterfaceWrapper::map_buffers(struct ipa_context *_ctx, for (unsigned int i = 0; i < num_buffers; ++i) { const struct ipa_buffer &_buffer = _buffers[i]; IPABuffer &buffer = buffers[i]; - std::vector &planes = buffer.memory.planes(); + std::vector &planes = buffer.planes; buffer.id = _buffer.id; planes.resize(_buffer.num_planes); for (unsigned int j = 0; j < _buffer.num_planes; ++j) { - if (_buffer.planes[j].dmabuf != -1) - planes[j].setDmabuf(_buffer.planes[j].dmabuf, - _buffer.planes[j].length); - /** \todo Create a Dmabuf class to implement RAII. */ - ::close(_buffer.planes[j].dmabuf); + planes[j].fd = FileDescriptor(_buffer.planes[j].dmabuf); + planes[j].length = _buffer.planes[j].length; } } diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 744a16ae5b9aa420..74b2922004bed556 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -48,7 +49,8 @@ private: void setControls(unsigned int frame); void metadataReady(unsigned int frame, unsigned int aeState); - std::map bufferInfo_; + std::map buffers_; + std::map buffersMemory_; ControlInfoMap ctrls_; @@ -102,15 +104,39 @@ void IPARkISP1::configure(const std::map &streamConfig, void IPARkISP1::mapBuffers(const std::vector &buffers) { for (const IPABuffer &buffer : buffers) { - bufferInfo_[buffer.id] = buffer.memory; - bufferInfo_[buffer.id].planes()[0].mem(); + auto elem = buffers_.emplace(buffer.id, buffer.planes); + const FrameBuffer &fb = elem.first->second; + + /* + * \todo Provide a helper to mmap() buffers (possibly exposed + * to applications). + */ + buffersMemory_[buffer.id] = mmap(NULL, + fb.planes()[0].length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fb.planes()[0].fd.fd(), + 0); + + if (buffersMemory_[buffer.id] == MAP_FAILED) { + int ret = -errno; + LOG(IPARkISP1, Fatal) << "Failed to mmap buffer: " + << strerror(-ret); + } } } void IPARkISP1::unmapBuffers(const std::vector &ids) { - for (unsigned int id : ids) - bufferInfo_.erase(id); + for (unsigned int id : ids) { + const auto fb = buffers_.find(id); + if (fb == buffers_.end()) + continue; + + munmap(buffersMemory_[id], fb->second.planes()[0].length); + buffersMemory_.erase(id); + buffers_.erase(id); + } } void IPARkISP1::processEvent(const IPAOperationData &event) @@ -121,7 +147,7 @@ void IPARkISP1::processEvent(const IPAOperationData &event) unsigned int bufferId = event.data[1]; const rkisp1_stat_buffer *stats = - static_cast(bufferInfo_[bufferId].planes()[0].mem()); + static_cast(buffersMemory_[bufferId]); updateStatistics(frame, stats); break; @@ -131,7 +157,7 @@ void IPARkISP1::processEvent(const IPAOperationData &event) unsigned int bufferId = event.data[1]; rkisp1_isp_params_cfg *params = - static_cast(bufferInfo_[bufferId].planes()[0].mem()); + static_cast(buffersMemory_[bufferId]); queueRequest(frame, params, event.controls[0]); break; diff --git a/src/libcamera/ipa_context_wrapper.cpp b/src/libcamera/ipa_context_wrapper.cpp index 9603fdac87150aa0..946a2fd8cab1782a 100644 --- a/src/libcamera/ipa_context_wrapper.cpp +++ b/src/libcamera/ipa_context_wrapper.cpp @@ -149,15 +149,15 @@ void IPAContextWrapper::mapBuffers(const std::vector &buffers) for (unsigned int i = 0; i < buffers.size(); ++i) { struct ipa_buffer &c_buffer = c_buffers[i]; const IPABuffer &buffer = buffers[i]; - const std::vector &planes = buffer.memory.planes(); + const std::vector &planes = buffer.planes; c_buffer.id = buffer.id; c_buffer.num_planes = planes.size(); for (unsigned int j = 0; j < planes.size(); ++j) { - const Plane &plane = planes[j]; - c_buffer.planes[j].dmabuf = plane.dmabuf(); - c_buffer.planes[j].length = plane.length(); + const FrameBuffer::Plane &plane = planes[j]; + c_buffer.planes[j].dmabuf = plane.fd.fd(); + c_buffer.planes[j].length = plane.length; } } diff --git a/src/libcamera/ipa_interface.cpp b/src/libcamera/ipa_interface.cpp index ee3e3622f39ae85f..2f86087ee4aa414f 100644 --- a/src/libcamera/ipa_interface.cpp +++ b/src/libcamera/ipa_interface.cpp @@ -334,11 +334,10 @@ namespace libcamera { */ /** - * \var IPABuffer::memory - * \brief The buffer memory description + * \var IPABuffer::planes + * \brief The buffer planes description * - * The memory field stores the dmabuf handle and size for each plane of the - * buffer. + * Stores the dmabuf handle and length for each plane of the buffer. */ /** diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 7e41222e3d01a475..d7ee95ded0f76027 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -687,14 +687,22 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, } for (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) { + FrameBuffer::Plane plane; + plane.fd = FileDescriptor(paramPool_.buffers()[i].planes()[0].dmabuf()); + plane.length = paramPool_.buffers()[i].planes()[0].length(); + data->ipaBuffers_.push_back({ .id = RKISP1_PARAM_BASE | i, - .memory = paramPool_.buffers()[i] }); + .planes = { plane } }); paramBuffers_.push(new Buffer(i)); } for (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) { + FrameBuffer::Plane plane; + plane.fd = FileDescriptor(statPool_.buffers()[i].planes()[0].dmabuf()); + plane.length = statPool_.buffers()[i].planes()[0].length(); + data->ipaBuffers_.push_back({ .id = RKISP1_STAT_BASE | i, - .memory = statPool_.buffers()[i] }); + .planes = { plane } }); statBuffers_.push(new Buffer(i)); } diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp index a1e34ad52317f47b..e711e4fe318d8179 100644 --- a/test/ipa/ipa_wrappers_test.cpp +++ b/test/ipa/ipa_wrappers_test.cpp @@ -115,28 +115,28 @@ public: return report(Op_mapBuffers, TestFail); } - if (buffers[0].memory.planes().size() != 3 || - buffers[1].memory.planes().size() != 3) { + if (buffers[0].planes.size() != 3 || + buffers[1].planes.size() != 3) { cerr << "mapBuffers(): Invalid number of planes" << endl; return report(Op_mapBuffers, TestFail); } - if (buffers[0].memory.planes()[0].length() != 4096 || - buffers[0].memory.planes()[1].length() != 0 || - buffers[0].memory.planes()[2].length() != 0 || - buffers[0].memory.planes()[0].length() != 4096 || - buffers[1].memory.planes()[1].length() != 4096 || - buffers[1].memory.planes()[2].length() != 0) { + if (buffers[0].planes[0].length != 4096 || + buffers[0].planes[1].length != 0 || + buffers[0].planes[2].length != 0 || + buffers[0].planes[0].length != 4096 || + buffers[1].planes[1].length != 4096 || + buffers[1].planes[2].length != 0) { cerr << "mapBuffers(): Invalid length" << endl; return report(Op_mapBuffers, TestFail); } - if (buffers[0].memory.planes()[0].dmabuf() == -1 || - buffers[0].memory.planes()[1].dmabuf() != -1 || - buffers[0].memory.planes()[2].dmabuf() != -1 || - buffers[0].memory.planes()[0].dmabuf() == -1 || - buffers[1].memory.planes()[1].dmabuf() == -1 || - buffers[1].memory.planes()[2].dmabuf() != -1) { + if (buffers[0].planes[0].fd.fd() == -1 || + buffers[0].planes[1].fd.fd() != -1 || + buffers[0].planes[2].fd.fd() != -1 || + buffers[0].planes[0].fd.fd() == -1 || + buffers[1].planes[1].fd.fd() == -1 || + buffers[1].planes[2].fd.fd() != -1) { cerr << "mapBuffers(): Invalid dmabuf" << endl; return report(Op_mapBuffers, TestFail); } @@ -287,13 +287,16 @@ protected: /* Test mapBuffers(). */ std::vector buffers(2); - buffers[0].memory.planes().resize(3); + buffers[0].planes.resize(3); buffers[0].id = 10; - buffers[0].memory.planes()[0].setDmabuf(fd_, 4096); + buffers[0].planes[0].fd = FileDescriptor(fd_); + buffers[0].planes[0].length = 4096; buffers[1].id = 11; - buffers[1].memory.planes().resize(3); - buffers[1].memory.planes()[0].setDmabuf(fd_, 4096); - buffers[1].memory.planes()[1].setDmabuf(fd_, 4096); + buffers[1].planes.resize(3); + buffers[1].planes[0].fd = FileDescriptor(fd_); + buffers[1].planes[0].length = 4096; + buffers[1].planes[1].fd = FileDescriptor(fd_); + buffers[1].planes[1].length = 4096; ret = INVOKE(mapBuffers, buffers); if (ret == TestFail) From patchwork Sun Jan 12 01:01:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2592 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E8ACE606CE for ; Sun, 12 Jan 2020 02:03:06 +0100 (CET) X-Halon-ID: 4864035b-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4864035b-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:02 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:50 +0100 Message-Id: <20200112010212.2609025-11-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 10/32] libcamera: buffer: Switch from Plane to FrameBuffer::Plane X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:07 -0000 It is not libcamera's responsibility to handle memory mappings. Switch from the soon to be removed Plane class which deals with memory mappings to FrameBuffer::Plane which just describes it. This makes the transition to the full FrameBuffer easier. As the full FrameBuffer interface has not yet spread to all parts of libcamera core it is hard to create efficient caching of memory mappings in the qcam application. This will be fixed in a later patch, for now the dmabuf is mapped and unmapped each time it is seen by the application. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- * Changes since v2 - Add todo about adding caching for cam mmap() call - Use {param,stat}Pool_.buffers()[i].planes() directly instead of creating a new plane. - Use push_back() instead of emplace_back() for already constructed objects. - s/todo:/todo/ --- include/libcamera/buffer.h | 6 +++--- src/cam/buffer_writer.cpp | 11 ++++++++--- src/libcamera/buffer.cpp | 4 ++-- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 12 ++---------- src/libcamera/stream.cpp | 6 ++++-- src/libcamera/v4l2_videodevice.cpp | 13 +++++++------ src/qcam/main_window.cpp | 14 ++++++++++---- src/v4l2/v4l2_camera.cpp | 2 +- test/camera/buffer_import.cpp | 2 +- 9 files changed, 38 insertions(+), 32 deletions(-) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index e66e9c9cf828160a..d61efad10f7d364d 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -95,11 +95,11 @@ private: class BufferMemory final { public: - const std::vector &planes() const { return planes_; } - std::vector &planes() { return planes_; } + const std::vector &planes() const { return planes_; } + std::vector &planes() { return planes_; } private: - std::vector planes_; + std::vector planes_; }; class BufferPool final diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp index c33e99c5f8173db8..765a176238e58dff 100644 --- a/src/cam/buffer_writer.cpp +++ b/src/cam/buffer_writer.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "buffer_writer.h" @@ -43,9 +44,11 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName) return -errno; BufferMemory *mem = buffer->mem(); - for (Plane &plane : mem->planes()) { - void *data = plane.mem(); - unsigned int length = plane.length(); + for (const FrameBuffer::Plane &plane : mem->planes()) { + /* \todo Once the FrameBuffer is done cache mapped memory. */ + void *data = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, + plane.fd.fd(), 0); + unsigned int length = plane.length; ret = ::write(fd, data, length); if (ret < 0) { @@ -59,6 +62,8 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName) << length << std::endl; break; } + + munmap(data, length); } close(fd); diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index 2178bd2fe17111dc..ec7c614dd654e4eb 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -257,13 +257,13 @@ void *Plane::mem() /** * \fn BufferMemory::planes() const * \brief Retrieve the planes within the buffer - * \return A const reference to a vector holding all Planes within the buffer + * \return A const reference to a vector holding all planes within the buffer */ /** * \fn BufferMemory::planes() * \brief Retrieve the planes within the buffer - * \return A reference to a vector holding all Planes within the buffer + * \return A reference to a vector holding all planes within the buffer */ /** diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index d7ee95ded0f76027..1e44571589a6003d 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -687,22 +687,14 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, } for (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) { - FrameBuffer::Plane plane; - plane.fd = FileDescriptor(paramPool_.buffers()[i].planes()[0].dmabuf()); - plane.length = paramPool_.buffers()[i].planes()[0].length(); - data->ipaBuffers_.push_back({ .id = RKISP1_PARAM_BASE | i, - .planes = { plane } }); + .planes = paramPool_.buffers()[i].planes() }); paramBuffers_.push(new Buffer(i)); } for (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) { - FrameBuffer::Plane plane; - plane.fd = FileDescriptor(statPool_.buffers()[i].planes()[0].dmabuf()); - plane.length = statPool_.buffers()[i].planes()[0].length(); - data->ipaBuffers_.push_back({ .id = RKISP1_STAT_BASE | i, - .planes = { plane } }); + .planes = statPool_.buffers()[i].planes() }); statBuffers_.push(new Buffer(i)); } diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index 45f31ae1e2daeb53..16a323f135b2022a 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -577,8 +577,10 @@ int Stream::mapBuffer(const Buffer *buffer) if (dmabufs[i] == -1) break; - mem->planes().emplace_back(); - mem->planes().back().setDmabuf(dmabufs[i], 0); + FrameBuffer::Plane plane; + plane.fd = FileDescriptor(dmabufs[i]); + plane.length = 0; + mem->planes().push_back(plane); } /* Remove the buffer from the cache and return its index. */ diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 033f7cc0d809ea8a..1dc9e19350f825a9 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -922,9 +922,10 @@ int V4L2VideoDevice::createPlane(BufferMemory *buffer, unsigned int index, return ret; } - buffer->planes().emplace_back(); - Plane &plane = buffer->planes().back(); - plane.setDmabuf(expbuf.fd, length); + FrameBuffer::Plane plane; + plane.fd = FileDescriptor(expbuf.fd); + plane.length = length; + buffer->planes().push_back(plane); ::close(expbuf.fd); return 0; @@ -987,14 +988,14 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer) bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type); BufferMemory *mem = &bufferPool_->buffers()[buf.index]; - const std::vector &planes = mem->planes(); + const std::vector &planes = mem->planes(); if (buf.memory == V4L2_MEMORY_DMABUF) { if (multiPlanar) { for (unsigned int p = 0; p < planes.size(); ++p) - v4l2Planes[p].m.fd = planes[p].dmabuf(); + v4l2Planes[p].m.fd = planes[p].fd.fd(); } else { - buf.m.fd = planes[0].dmabuf(); + buf.m.fd = planes[0].fd.fd(); } } diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 0c7ca61ac12ec41c..0a353e8ba247caf9 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -296,13 +297,18 @@ void MainWindow::requestComplete(Request *request) int MainWindow::display(Buffer *buffer) { - BufferMemory *mem = buffer->mem(); - if (mem->planes().size() != 1) + if (buffer->mem()->planes().size() != 1) return -EINVAL; - Plane &plane = mem->planes().front(); - unsigned char *raw = static_cast(plane.mem()); + /* \todo Once the FrameBuffer is done cache mapped memory. */ + const FrameBuffer::Plane &plane = buffer->mem()->planes().front(); + void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, + plane.fd.fd(), 0); + + unsigned char *raw = static_cast(memory); viewfinder_->display(raw, buffer->bytesused()); + munmap(memory, plane.length); + return 0; } diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 07f05a31261c1b25..7221755ddb5db4bf 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -135,7 +135,7 @@ void V4L2Camera::freeBuffers() FileDescriptor V4L2Camera::getBufferFd(unsigned int index) { Stream *stream = *camera_->streams().begin(); - return FileDescriptor(stream->buffers()[index].planes()[0].dmabuf()); + return stream->buffers()[index].planes()[0].fd; } int V4L2Camera::streamOn() diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index 3efe02704c02f691..171540edd96f9fca 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -178,7 +178,7 @@ private: uint64_t cookie = index; BufferMemory &mem = pool_.buffers()[index]; - int dmabuf = mem.planes()[0].dmabuf(); + int dmabuf = mem.planes()[0].fd.fd(); requestReady.emit(cookie, dmabuf); From patchwork Sun Jan 12 01:01:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2593 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BF5BD606D6 for ; Sun, 12 Jan 2020 02:03:07 +0100 (CET) X-Halon-ID: 490f970c-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 490f970c-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:03 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:51 +0100 Message-Id: <20200112010212.2609025-12-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 11/32] libcamera: buffers: Remove Plane class X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:08 -0000 There are no users left of the Plane class, drop it. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/buffer.h | 21 ------ src/libcamera/buffer.cpp | 149 ------------------------------------- 2 files changed, 170 deletions(-) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index d61efad10f7d364d..d3dc9a420aaaa385 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -71,27 +71,6 @@ private: unsigned int cookie_; }; -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 BufferMemory final { public: diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index ec7c614dd654e4eb..d53e5150b40a3b09 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -96,155 +96,6 @@ LOG_DEFINE_CATEGORY(Buffer) * \brief Array of per-plane metadata */ -/** - * \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 code 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 code 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 code 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() const - * \brief Retrieve the length of the memory region - * \return The length of the memory region - */ - /** * \class BufferMemory * \brief A memory buffer to store an image From patchwork Sun Jan 12 01:01:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2594 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7CB38606D6 for ; Sun, 12 Jan 2020 02:03:08 +0100 (CET) X-Halon-ID: 498fef40-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 498fef40-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:04 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:52 +0100 Message-Id: <20200112010212.2609025-13-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 12/32] libcamera: buffer: Drop private function setRequest() X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:08 -0000 There is no need to have a private helper function to access a private data member when a friend statement is needed anyhow. Remove the helper function to simplify the code and make it clear that a private member of Buffer is accessed. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/buffer.h | 2 -- src/libcamera/buffer.cpp | 8 ++++---- src/libcamera/request.cpp | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index d3dc9a420aaaa385..55d08e278a7d0236 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -129,8 +129,6 @@ private: void cancel(); - void setRequest(Request *request) { request_ = request; } - unsigned int index_; std::array dmabuf_; BufferMemory *mem_; diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index d53e5150b40a3b09..8c8be4ac802735b1 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -290,7 +290,6 @@ Buffer::Buffer(unsigned int index, const Buffer *metadata) * * \return The Request the Buffer belongs to, or nullptr if the buffer is * either completed or not associated with a request - * \sa Buffer::setRequest() */ /** @@ -318,10 +317,11 @@ void Buffer::cancel() } /** - * \fn Buffer::setRequest() - * \brief Set the request this buffer belongs to + * \var Buffer::request_ + * \brief The request this buffer belongs to * - * The intended callers are Request::addBuffer() and Request::completeBuffer(). + * This member is intended to be set by Request::addBuffer() and + * Request::completeBuffer(). */ /** diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 84a5f55879ccf5b5..92330c1a37290fc2 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -139,7 +139,7 @@ int Request::addBuffer(std::unique_ptr buffer) return -EEXIST; } - buffer->setRequest(this); + buffer->request_ = this; pending_.insert(buffer.get()); bufferMap_[stream] = buffer.release(); @@ -236,7 +236,7 @@ bool Request::completeBuffer(Buffer *buffer) int ret = pending_.erase(buffer); ASSERT(ret == 1); - buffer->setRequest(nullptr); + buffer->request_ = nullptr; if (buffer->status() == Buffer::BufferCancelled) cancelled_ = true; From patchwork Sun Jan 12 01:01:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2595 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 20C65606D6 for ; Sun, 12 Jan 2020 02:03:09 +0100 (CET) X-Halon-ID: 4a020e0e-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4a020e0e-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:05 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:53 +0100 Message-Id: <20200112010212.2609025-14-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 13/32] libcamera: v4l2_videodevice: Align which type variable is used in queueBuffer() X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:09 -0000 Reading V4L2VideoDevice::queueBuffer() is confusing since buf.type is first set to bufferType_ but then both variables are used in V4L2 macros to operate based on which type of buffer is being processed. Align on only using buf.type since it has the most existing users. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/libcamera/v4l2_videodevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 1dc9e19350f825a9..81d999e354a16bd0 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -1004,7 +1004,7 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer) buf.m.planes = v4l2Planes; } - if (V4L2_TYPE_IS_OUTPUT(bufferType_)) { + if (V4L2_TYPE_IS_OUTPUT(buf.type)) { buf.bytesused = buffer->bytesused_; buf.sequence = buffer->sequence_; buf.timestamp.tv_sec = buffer->timestamp_ / 1000000000; From patchwork Sun Jan 12 01:01:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2596 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CEFC7606D6 for ; Sun, 12 Jan 2020 02:03:09 +0100 (CET) X-Halon-ID: 4a600745-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4a600745-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:06 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:54 +0100 Message-Id: <20200112010212.2609025-15-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 14/32] libcamera: v4l2_videodevice: Extract exportDmabufFd() X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:10 -0000 The part in createPlane() that exports a dma buffer from a video device will be used directly by the FrameBuffer interface. Break it out to a separate function. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- * Changes since v3 - Make exportDmabufFd() return a FileDescriptor * Changes since v1 - Rename exportDmaBuffer() to exportDmabufFd() --- src/libcamera/include/v4l2_videodevice.h | 2 ++ src/libcamera/v4l2_videodevice.cpp | 42 +++++++++++++++--------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index fdf11b3a6ec9b702..27ec77cdcc3c07ff 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -26,6 +26,7 @@ class Buffer; class BufferMemory; class BufferPool; class EventNotifier; +class FileDescriptor; class MediaDevice; class MediaEntity; @@ -179,6 +180,7 @@ private: int requestBuffers(unsigned int count); int createPlane(BufferMemory *buffer, unsigned int index, unsigned int plane, unsigned int length); + FileDescriptor exportDmabufFd(unsigned int index, unsigned int plane); Buffer *dequeueBuffer(); void bufferAvailable(EventNotifier *notifier); diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 81d999e354a16bd0..4551484cfbf8c6ef 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "log.h" #include "media_device.h" @@ -902,31 +903,19 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool) int V4L2VideoDevice::createPlane(BufferMemory *buffer, unsigned int index, unsigned int planeIndex, unsigned int length) { - struct v4l2_exportbuffer expbuf = {}; - int ret; - LOG(V4L2, Debug) << "Buffer " << index << " plane " << planeIndex << ": length=" << length; - expbuf.type = bufferType_; - expbuf.index = index; - expbuf.plane = planeIndex; - expbuf.flags = O_RDWR; - - ret = ioctl(VIDIOC_EXPBUF, &expbuf); - if (ret < 0) { - LOG(V4L2, Error) - << "Failed to export buffer: " << strerror(-ret); - return ret; - } + FileDescriptor fd = exportDmabufFd(index, planeIndex); + if (!fd.isValid()) + return -EINVAL; FrameBuffer::Plane plane; - plane.fd = FileDescriptor(expbuf.fd); + plane.fd = fd; plane.length = length; buffer->planes().push_back(plane); - ::close(expbuf.fd); return 0; } @@ -952,6 +941,27 @@ int V4L2VideoDevice::importBuffers(BufferPool *pool) return 0; } +FileDescriptor V4L2VideoDevice::exportDmabufFd(unsigned int index, + unsigned int plane) +{ + struct v4l2_exportbuffer expbuf = {}; + int ret; + + expbuf.type = bufferType_; + expbuf.index = index; + expbuf.plane = plane; + expbuf.flags = O_RDWR; + + ret = ioctl(VIDIOC_EXPBUF, &expbuf); + if (ret < 0) { + LOG(V4L2, Error) + << "Failed to export buffer: " << strerror(-ret); + return FileDescriptor(); + } + + return FileDescriptor(expbuf.fd); +} + /** * \brief Release all internally allocated buffers */ From patchwork Sun Jan 12 01:01:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2597 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C3BF1606DA for ; Sun, 12 Jan 2020 02:03:10 +0100 (CET) X-Halon-ID: 4ad63666-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4ad63666-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:07 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:55 +0100 Message-Id: <20200112010212.2609025-16-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 15/32] libcamera: request: In addBuffer() do not fetch stream from Buffer X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:11 -0000 In the FrameBuffer interface the stream will not be available from the buffer object as the buffer might be allocated externally. The application needs to explicitly state which stream the buffer is being added for to the request. Extend the addBuffer() function to get this information explicitly from the caller. Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/request.h | 2 +- src/android/camera_device.cpp | 2 +- src/cam/capture.cpp | 4 ++-- src/libcamera/request.cpp | 4 ++-- src/qcam/main_window.cpp | 4 ++-- src/v4l2/v4l2_camera.cpp | 2 +- test/camera/buffer_import.cpp | 2 +- test/camera/capture.cpp | 4 ++-- test/camera/statemachine.cpp | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 728f380de4f010c3..b832422482645f25 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -39,7 +39,7 @@ public: ControlList &controls() { return *controls_; } ControlList &metadata() { return *metadata_; } const std::map &buffers() const { return bufferMap_; } - int addBuffer(std::unique_ptr buffer); + int addBuffer(Stream *stream, std::unique_ptr buffer); Buffer *findBuffer(Stream *stream) const; uint64_t cookie() const { return cookie_; } diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 065e0292e186c2ad..09588c16a6301649 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -754,7 +754,7 @@ void CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reque Request *request = camera_->createRequest(reinterpret_cast(descriptor)); - request->addBuffer(std::move(buffer)); + request->addBuffer(stream, std::move(buffer)); int ret = camera_->queueRequest(request); if (ret) { diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp index 4b65b1d0a2dbed35..1a4dbe7ce4a15a2d 100644 --- a/src/cam/capture.cpp +++ b/src/cam/capture.cpp @@ -95,7 +95,7 @@ int Capture::capture(EventLoop *loop) Stream *stream = cfg.stream(); std::unique_ptr buffer = stream->createBuffer(i); - ret = request->addBuffer(std::move(buffer)); + ret = request->addBuffer(stream, std::move(buffer)); if (ret < 0) { std::cerr << "Can't set buffer for request" << std::endl; @@ -185,7 +185,7 @@ void Capture::requestComplete(Request *request) return; } - request->addBuffer(std::move(newBuffer)); + request->addBuffer(stream, std::move(newBuffer)); } camera_->queueRequest(request); diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 92330c1a37290fc2..54dfb461c58b6971 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -113,6 +113,7 @@ Request::~Request() /** * \brief Store a Buffer with its associated Stream in the Request + * \param[in] stream The stream the buffer belongs to * \param[in] buffer The Buffer to store in the request * * Ownership of the buffer is passed to the request. It will be deleted when @@ -125,9 +126,8 @@ Request::~Request() * \retval -EEXIST The request already contains a buffer for the stream * \retval -EINVAL The buffer does not reference a valid Stream */ -int Request::addBuffer(std::unique_ptr buffer) +int Request::addBuffer(Stream *stream, std::unique_ptr buffer) { - Stream *stream = buffer->stream(); if (!stream) { LOG(Request, Error) << "Invalid stream reference"; return -EINVAL; diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 0a353e8ba247caf9..8b3d99237047a9e5 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -191,7 +191,7 @@ int MainWindow::startCapture() goto error; } - ret = request->addBuffer(std::move(buffer)); + ret = request->addBuffer(stream, std::move(buffer)); if (ret < 0) { std::cerr << "Can't set buffer for request" << std::endl; goto error; @@ -289,7 +289,7 @@ void MainWindow::requestComplete(Request *request) return; } - request->addBuffer(std::move(newBuffer)); + request->addBuffer(stream, std::move(newBuffer)); } camera_->queueRequest(request); diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 7221755ddb5db4bf..b6e0bb762bcd79fe 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -192,7 +192,7 @@ int V4L2Camera::qbuf(unsigned int index) return -ENOMEM; } - int ret = request->addBuffer(std::move(buffer)); + int ret = request->addBuffer(stream, std::move(buffer)); if (ret < 0) { LOG(V4L2Compat, Error) << "Can't set buffer for request"; return -ENOMEM; diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index 171540edd96f9fca..e5c010d81b8d6e0e 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -268,7 +268,7 @@ public: Request *request = camera_->createRequest(cookie); std::unique_ptr buffer = stream_->createBuffer({ dmabuf, -1, -1 }); - request->addBuffer(move(buffer)); + request->addBuffer(stream_, move(buffer)); camera_->queueRequest(request); } diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp index 7cb76038f557d461..ca1ebe419946dd4d 100644 --- a/test/camera/capture.cpp +++ b/test/camera/capture.cpp @@ -49,7 +49,7 @@ protected: std::unique_ptr newBuffer = stream->createBuffer(buffer->index()); request = camera_->createRequest(); - request->addBuffer(std::move(newBuffer)); + request->addBuffer(stream, std::move(newBuffer)); camera_->queueRequest(request); } @@ -101,7 +101,7 @@ protected: return TestFail; } - if (request->addBuffer(std::move(buffer))) { + if (request->addBuffer(stream, std::move(buffer))) { cout << "Failed to associating buffer with request" << endl; return TestFail; } diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp index afa13ba77b0b7fe0..f627b8f37422350e 100644 --- a/test/camera/statemachine.cpp +++ b/test/camera/statemachine.cpp @@ -219,7 +219,7 @@ protected: Stream *stream = *camera_->streams().begin(); std::unique_ptr buffer = stream->createBuffer(0); - if (request->addBuffer(std::move(buffer))) + if (request->addBuffer(stream, std::move(buffer))) return TestFail; if (camera_->queueRequest(request)) From patchwork Sun Jan 12 01:01:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2598 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 24C03606DF for ; Sun, 12 Jan 2020 02:03:12 +0100 (CET) X-Halon-ID: 4b65c7d7-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4b65c7d7-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:07 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:56 +0100 Message-Id: <20200112010212.2609025-17-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 16/32] libcamera: buffer: Move captured metadata to FrameMetadata X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:12 -0000 Move the metadata retrieved when dequeuing a V4L2 buffer into a FrameMetadata object. This is done as a step to migrate to the FrameBuffer interface as the functions added to Buffer around FrameMetadata match the ones in FrameBuffer. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v2 - Syntax improvement. - Improved documentation. * Changes since v1 - Rework to match FrameMetadata being a struct instead of a class - Align statements broken over multiple lines - Spiffy up bytesused printing in cam - Merged with patch who removes the old fields in Buffer. --- include/libcamera/buffer.h | 16 +---- src/android/camera_device.cpp | 4 +- src/cam/buffer_writer.cpp | 2 +- src/cam/capture.cpp | 13 +++- src/libcamera/buffer.cpp | 76 +++++------------------- src/libcamera/pipeline/ipu3/ipu3.cpp | 4 +- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 8 +-- src/libcamera/request.cpp | 2 +- src/libcamera/v4l2_videodevice.cpp | 25 ++++---- src/qcam/main_window.cpp | 13 ++-- src/v4l2/v4l2_camera.cpp | 8 ++- src/v4l2/v4l2_camera.h | 4 +- src/v4l2/v4l2_camera_proxy.cpp | 4 +- test/camera/buffer_import.cpp | 2 +- test/camera/capture.cpp | 2 +- test/v4l2_videodevice/buffer_sharing.cpp | 8 ++- 16 files changed, 78 insertions(+), 113 deletions(-) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index 55d08e278a7d0236..0eb84c32cc8570c5 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -99,12 +99,6 @@ private: class Buffer final { public: - enum Status { - BufferSuccess, - BufferError, - BufferCancelled, - }; - Buffer(unsigned int index = -1, const Buffer *metadata = nullptr); Buffer(const Buffer &) = delete; Buffer &operator=(const Buffer &) = delete; @@ -113,11 +107,8 @@ public: const std::array &dmabufs() const { return dmabuf_; } BufferMemory *mem() { return mem_; } - unsigned int bytesused() const { return bytesused_; } - uint64_t timestamp() const { return timestamp_; } - unsigned int sequence() const { return sequence_; } + const FrameMetadata &metadata() const { return metadata_; }; - Status status() const { return status_; } Request *request() const { return request_; } Stream *stream() const { return stream_; } @@ -133,11 +124,8 @@ private: std::array dmabuf_; BufferMemory *mem_; - unsigned int bytesused_; - uint64_t timestamp_; - unsigned int sequence_; + FrameMetadata metadata_; - Status status_; Request *request_; Stream *stream_; }; diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 09588c16a6301649..ebe91ea8af4f6436 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -803,11 +803,11 @@ void CameraDevice::requestComplete(Request *request) if (status == CAMERA3_BUFFER_STATUS_OK) { notifyShutter(descriptor->frameNumber, - libcameraBuffer->timestamp()); + libcameraBuffer->metadata().timestamp); captureResult.partial_result = 1; resultMetadata = getResultMetadata(descriptor->frameNumber, - libcameraBuffer->timestamp()); + libcameraBuffer->metadata().timestamp); captureResult.result = resultMetadata->get(); } diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp index 765a176238e58dff..41ef4b0a36d61b03 100644 --- a/src/cam/buffer_writer.cpp +++ b/src/cam/buffer_writer.cpp @@ -33,7 +33,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName) if (pos != std::string::npos) { std::stringstream ss; ss << streamName << "-" << std::setw(6) - << std::setfill('0') << buffer->sequence(); + << std::setfill('0') << buffer->metadata().sequence; filename.replace(pos, 1, ss.str()); } diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp index 1a4dbe7ce4a15a2d..da942f56118983bd 100644 --- a/src/cam/capture.cpp +++ b/src/cam/capture.cpp @@ -154,9 +154,18 @@ void Capture::requestComplete(Request *request) Buffer *buffer = it->second; const std::string &name = streamName_[stream]; + const FrameMetadata &metadata = buffer->metadata(); + info << " " << name - << " seq: " << std::setw(6) << std::setfill('0') << buffer->sequence() - << " bytesused: " << buffer->bytesused(); + << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence + << " bytesused: "; + + unsigned int nplane = 0; + for (const FrameMetadata::Plane &plane : metadata.planes) { + info << plane.bytesused; + if (++nplane < metadata.planes.size()) + info << "/"; + } if (writer_) writer_->write(buffer, name); diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index 8c8be4ac802735b1..92ac28387bd4c4f7 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -177,20 +177,6 @@ void BufferPool::destroyBuffers() * deleted automatically after the request complete handler returns. */ -/** - * \enum Buffer::Status - * Buffer completion status - * \var Buffer::BufferSuccess - * The buffer has completed with success and contains valid data. All its other - * metadata (such as bytesused(), timestamp() or sequence() number) are valid. - * \var Buffer::BufferError - * The buffer has completed with an error and doesn't contain valid data. Its - * other metadata are valid. - * \var Buffer::BufferCancelled - * The buffer has been cancelled due to capture stop. Its other metadata are - * invalid and shall not be used. - */ - /** * \brief Construct a buffer not associated with any stream * @@ -199,19 +185,15 @@ void BufferPool::destroyBuffers() * for a stream with Stream::createBuffer(). */ Buffer::Buffer(unsigned int index, const Buffer *metadata) - : index_(index), dmabuf_({ -1, -1, -1 }), - status_(Buffer::BufferSuccess), request_(nullptr), + : index_(index), dmabuf_({ -1, -1, -1 }), request_(nullptr), stream_(nullptr) { - if (metadata) { - bytesused_ = metadata->bytesused_; - sequence_ = metadata->sequence_; - timestamp_ = metadata->timestamp_; - } else { - bytesused_ = 0; - sequence_ = 0; - timestamp_ = 0; - } + if (metadata) + metadata_ = metadata->metadata(); + else + metadata_ = {}; + + metadata_.status = FrameMetadata::FrameSuccess; } /** @@ -242,39 +224,13 @@ Buffer::Buffer(unsigned int index, const Buffer *metadata) */ /** - * \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 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 - */ - -/** - * \fn Buffer::status() - * \brief Retrieve the buffer status + * \fn Buffer::metadata() + * \brief Retrieve the buffer metadata * - * The buffer status reports whether the buffer has completed successfully - * (BufferSuccess) or if an error occurred (BufferError). + * The buffer metadata is updated when the buffer contents are modified, for + * example when a frame has been captured to the buffer by the hardware. * - * \return The buffer status + * \return Metadata for the buffer */ /** @@ -310,10 +266,10 @@ Buffer::Buffer(unsigned int index, const Buffer *metadata) */ void Buffer::cancel() { - bytesused_ = 0; - timestamp_ = 0; - sequence_ = 0; - status_ = BufferCancelled; + metadata_.status = FrameMetadata::FrameCancelled; + metadata_.sequence = 0; + metadata_.timestamp = 0; + metadata_.planes = {}; } /** diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 6d8c3fada127310e..34fc792977d151be 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -928,7 +928,7 @@ int PipelineHandlerIPU3::registerCameras() void IPU3CameraData::imguInputBufferReady(Buffer *buffer) { /* \todo Handle buffer failures when state is set to BufferError. */ - if (buffer->status() == Buffer::BufferCancelled) + if (buffer->metadata().status == FrameMetadata::FrameCancelled) return; cio2_.output_->queueBuffer(buffer); @@ -962,7 +962,7 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer) void IPU3CameraData::cio2BufferReady(Buffer *buffer) { /* \todo Handle buffer failures when state is set to BufferError. */ - if (buffer->status() == Buffer::BufferCancelled) + if (buffer->metadata().status == FrameMetadata::FrameCancelled) return; imgu_->input_->queueBuffer(buffer); diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 1e44571589a6003d..979b670e4cb75512 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -100,10 +100,10 @@ public: ASSERT(frameOffset(SOE) == 0); utils::time_point soe = std::chrono::time_point() - + std::chrono::nanoseconds(buffer->timestamp()) + + std::chrono::nanoseconds(buffer->metadata().timestamp) + timeOffset(SOE); - notifyStartOfExposure(buffer->sequence(), soe); + notifyStartOfExposure(buffer->metadata().sequence, soe); } void setDelay(unsigned int type, int frame, int msdelay) @@ -994,8 +994,8 @@ void PipelineHandlerRkISP1::bufferReady(Buffer *buffer) data->timeline_.bufferReady(buffer); - if (data->frame_ <= buffer->sequence()) - data->frame_ = buffer->sequence() + 1; + if (data->frame_ <= buffer->metadata().sequence) + data->frame_ = buffer->metadata().sequence + 1; completeBuffer(activeCamera_, request, buffer); tryCompleteRequest(request); diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 54dfb461c58b6971..4268e31434bf4feb 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -238,7 +238,7 @@ bool Request::completeBuffer(Buffer *buffer) buffer->request_ = nullptr; - if (buffer->status() == Buffer::BufferCancelled) + if (buffer->metadata().status == FrameMetadata::FrameCancelled) cancelled_ = true; return !hasPendingBuffers(); diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 4551484cfbf8c6ef..d22655c676bef1ae 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -1015,10 +1015,13 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer) } if (V4L2_TYPE_IS_OUTPUT(buf.type)) { - buf.bytesused = buffer->bytesused_; - buf.sequence = buffer->sequence_; - buf.timestamp.tv_sec = buffer->timestamp_ / 1000000000; - buf.timestamp.tv_usec = (buffer->timestamp_ / 1000) % 1000000; + const FrameMetadata &metadata = buffer->metadata(); + + if (!metadata.planes.empty()) + buf.bytesused = metadata.planes[0].bytesused; + buf.sequence = metadata.sequence; + buf.timestamp.tv_sec = metadata.timestamp / 1000000000; + buf.timestamp.tv_usec = (metadata.timestamp / 1000) % 1000000; } LOG(V4L2, Debug) << "Queueing buffer " << buf.index; @@ -1125,12 +1128,14 @@ Buffer *V4L2VideoDevice::dequeueBuffer() fdEvent_->setEnabled(false); buffer->index_ = buf.index; - buffer->bytesused_ = buf.bytesused; - buffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL - + buf.timestamp.tv_usec * 1000ULL; - buffer->sequence_ = buf.sequence; - buffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR - ? Buffer::BufferError : Buffer::BufferSuccess; + + buffer->metadata_.status = buf.flags & V4L2_BUF_FLAG_ERROR + ? FrameMetadata::FrameError + : FrameMetadata::FrameSuccess; + buffer->metadata_.sequence = buf.sequence; + buffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL + + buf.timestamp.tv_usec * 1000ULL; + buffer->metadata_.planes = { { buf.bytesused } }; return buffer; } diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 8b3d99237047a9e5..ca3464babdc1c313 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -259,14 +259,15 @@ void MainWindow::requestComplete(Request *request) framesCaptured_++; Buffer *buffer = buffers.begin()->second; + const FrameMetadata &metadata = buffer->metadata(); - double fps = buffer->timestamp() - lastBufferTime_; + double fps = metadata.timestamp - lastBufferTime_; fps = lastBufferTime_ && fps ? 1000000000.0 / fps : 0.0; - lastBufferTime_ = buffer->timestamp(); + lastBufferTime_ = metadata.timestamp; - std::cout << "seq: " << std::setw(6) << std::setfill('0') << buffer->sequence() - << " bytesused: " << buffer->bytesused() - << " timestamp: " << buffer->timestamp() + std::cout << "seq: " << std::setw(6) << std::setfill('0') << metadata.sequence + << " bytesused: " << metadata.planes[0].bytesused + << " timestamp: " << metadata.timestamp << " fps: " << std::fixed << std::setprecision(2) << fps << std::endl; @@ -306,7 +307,7 @@ int MainWindow::display(Buffer *buffer) plane.fd.fd(), 0); unsigned char *raw = static_cast(memory); - viewfinder_->display(raw, buffer->bytesused()); + viewfinder_->display(raw, buffer->metadata().planes[0].bytesused); munmap(memory, plane.length); diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index b6e0bb762bcd79fe..e4a03c414480f353 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -17,9 +17,11 @@ using namespace libcamera; LOG_DECLARE_CATEGORY(V4L2Compat); V4L2FrameMetadata::V4L2FrameMetadata(Buffer *buffer) - : index_(buffer->index()), bytesused_(buffer->bytesused()), - timestamp_(buffer->timestamp()), sequence_(buffer->sequence()), - status_(buffer->status()) + : index_(buffer->index()), + bytesused_(buffer->metadata().planes[0].bytesused), + timestamp_(buffer->metadata().timestamp), + sequence_(buffer->metadata().sequence), + status_(buffer->metadata().status) { } diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h index f760316c854fba2f..06eab0e1c93b7c8a 100644 --- a/src/v4l2/v4l2_camera.h +++ b/src/v4l2/v4l2_camera.h @@ -31,7 +31,7 @@ public: uint64_t timestamp() const { return timestamp_; } unsigned int sequence() const { return sequence_; } - Buffer::Status status() const { return status_; } + FrameMetadata::Status status() const { return status_; } private: int index_; @@ -40,7 +40,7 @@ private: uint64_t timestamp_; unsigned int sequence_; - Buffer::Status status_; + FrameMetadata::Status status_; }; class V4L2Camera : public Object diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp index e4aba33e6d33f21b..59cf360edd28f69c 100644 --- a/src/v4l2/v4l2_camera_proxy.cpp +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -192,7 +192,7 @@ void V4L2CameraProxy::updateBuffers() struct v4l2_buffer &buf = buffers_[fmd.index()]; switch (fmd.status()) { - case Buffer::Status::BufferSuccess: + case FrameMetadata::FrameSuccess: buf.bytesused = fmd.bytesused(); buf.field = V4L2_FIELD_NONE; buf.timestamp.tv_sec = fmd.timestamp() / 1000000000; @@ -201,7 +201,7 @@ void V4L2CameraProxy::updateBuffers() buf.flags |= V4L2_BUF_FLAG_DONE; break; - case Buffer::Status::BufferError: + case FrameMetadata::FrameError: buf.flags |= V4L2_BUF_FLAG_ERROR; break; default: diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index e5c010d81b8d6e0e..3ba6ce9690f29329 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -275,7 +275,7 @@ public: protected: void bufferComplete(Request *request, Buffer *buffer) { - if (buffer->status() != Buffer::BufferSuccess) + if (buffer->metadata().status != FrameMetadata::FrameSuccess) return; unsigned int index = buffer->index(); diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp index ca1ebe419946dd4d..0d9ffc476650f414 100644 --- a/test/camera/capture.cpp +++ b/test/camera/capture.cpp @@ -28,7 +28,7 @@ protected: void bufferComplete(Request *request, Buffer *buffer) { - if (buffer->status() != Buffer::BufferSuccess) + if (buffer->metadata().status != FrameMetadata::FrameSuccess) return; completeBuffersCount_++; diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp index 3a56862cb2b77d38..fe48b2e98fdada8d 100644 --- a/test/v4l2_videodevice/buffer_sharing.cpp +++ b/test/v4l2_videodevice/buffer_sharing.cpp @@ -92,9 +92,11 @@ protected: void captureBufferReady(Buffer *buffer) { + const FrameMetadata &metadata = buffer->metadata(); + std::cout << "Received capture buffer" << std::endl; - if (buffer->status() != Buffer::BufferSuccess) + if (metadata.status != FrameMetadata::FrameSuccess) return; output_->queueBuffer(buffer); @@ -103,9 +105,11 @@ protected: void outputBufferReady(Buffer *buffer) { + const FrameMetadata &metadata = buffer->metadata(); + std::cout << "Received output buffer" << std::endl; - if (buffer->status() != Buffer::BufferSuccess) + if (metadata.status != FrameMetadata::FrameSuccess) return; capture_->queueBuffer(buffer); From patchwork Sun Jan 12 01:01:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2599 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0FAF8606DF for ; Sun, 12 Jan 2020 02:03:13 +0100 (CET) X-Halon-ID: 4c2f8717-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4c2f8717-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:09 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:57 +0100 Message-Id: <20200112010212.2609025-18-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 17/32] libcamera: v4l2_videodevice: Add V4L2BufferCache to deal with index mapping X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:13 -0000 In preparation for the FrameBuffer interface add a class that will deal with keeping the cache between dmabuf file descriptors and V4L2 video device buffer indexes. This initial implementation ensures that no hot association is lost while its eviction strategy could be improved in the future. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v3 - Only print cach misses if it's more then cache_.size() - Invert hit/miss logic in V4L2BufferCache::get() * Changes since v2 - Entry constructor and operator== takes FrameBuffer instead of std::vector. - Move Entry inline functions to from .h to .cpp file - Make Entry::Plane a struct - Add a miss counter * Changes since v1 - fetch() take a const reference instead of a const pointer - Rename argument for V4L2BufferCache() - Rename V4L2BufferCache::CacheInfo to V4L2BufferCache::Entry. - Turn V4L2BufferCache::Entry into a class with constructors for foo.emplace_back(). - Rename V4L2BufferCache::fetch() to V4L2BufferCache::get() - Make use of operator== - Large updates of documentation. --- src/libcamera/include/v4l2_videodevice.h | 45 +++++++- src/libcamera/v4l2_videodevice.cpp | 136 ++++++++++++++++++++++- 2 files changed, 177 insertions(+), 4 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index 27ec77cdcc3c07ff..9d22754a39a75621 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -11,7 +11,9 @@ #include #include +#include +#include #include #include #include @@ -22,9 +24,6 @@ namespace libcamera { -class Buffer; -class BufferMemory; -class BufferPool; class EventNotifier; class FileDescriptor; class MediaDevice; @@ -106,6 +105,46 @@ struct V4L2Capability final : v4l2_capability { } }; +class V4L2BufferCache +{ +public: + V4L2BufferCache(unsigned int numEntries); + V4L2BufferCache(const std::vector> &buffers); + ~V4L2BufferCache(); + + int get(const FrameBuffer &buffer); + void put(unsigned int index); + +private: + class Entry + { + public: + Entry(); + Entry(bool free, const FrameBuffer &buffer); + + bool operator==(const FrameBuffer &buffer); + + bool free; + + private: + struct Plane { + Plane(const FrameBuffer::Plane &plane) + : fd(plane.fd.fd()), length(plane.length) + { + } + + int fd; + unsigned int length; + }; + + std::vector planes_; + }; + + std::vector cache_; + /* \todo Expose the miss counter through an instrumentation API. */ + unsigned int missCounter_; +}; + class V4L2DeviceFormat { public: diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index d22655c676bef1ae..84c45dbcb85c8638 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -20,7 +20,6 @@ #include -#include #include #include @@ -136,6 +135,141 @@ LOG_DECLARE_CATEGORY(V4L2) * \return True if the video device provides Streaming I/O IOCTLs */ +/** + * \class V4L2BufferCache + * \brief Hot cache of associations between V4L2 buffer indexes and FrameBuffer + * + * When importing buffers, V4L2 performs lazy mapping of dmabuf instances at + * VIDIOC_QBUF (or VIDIOC_PREPARE_BUF) time and keeps the mapping associated + * with the V4L2 buffer, as identified by its index. If the same V4L2 buffer is + * then reused and queued with different dmabufs, the old dmabufs will be + * unmapped and the new ones mapped. To keep this process efficient, it is + * crucial to consistently use the same V4L2 buffer for given dmabufs through + * the whole duration of a capture cycle. + * + * The V4L2BufferCache class keeps a map of previous dmabufs to V4L2 buffer + * index associations to help selecting V4L2 buffers. It tracks, for every + * entry, if the V4L2 buffer is in use, and offers lookup of the best free V4L2 + * buffer for a set of dmabufs. + */ + +/** + * \brief Create an empty cache with \a numEntries entries + * \param[in] numEntries Number of entries to reserve in the cache + * + * Create a cache with \a numEntries entries all marked as unused. The entries + * will be populated as the cache is used. This is typically used to implement + * buffer import, with buffers added to the cache as they are queued. + */ +V4L2BufferCache::V4L2BufferCache(unsigned int numEntries) + : missCounter_(0) +{ + cache_.resize(numEntries); +} + +/** + * \brief Create a pre-populated cache + * \param[in] buffers Array of buffers to pre-populated with + * + * Create a cache pre-populated with \a buffers. This is typically used to + * implement buffer export, with all buffers added to the cache when they are + * allocated. + */ +V4L2BufferCache::V4L2BufferCache(const std::vector> &buffers) + : missCounter_(0) +{ + for (const std::unique_ptr &buffer : buffers) + cache_.emplace_back(true, buffer->planes()); +} + +V4L2BufferCache::~V4L2BufferCache() +{ + if (missCounter_ > cache_.size()) + LOG(V4L2, Debug) << "Cache misses: " << missCounter_; +} + +/** + * \brief Find the best V4L2 buffer for a FrameBuffer + * \param[in] buffer The FrameBuffer + * + * Find the best V4L2 buffer index to be used for the FrameBuffer \a buffer + * based on previous mappings of frame buffers to V4L2 buffers. If a free V4L2 + * buffer previously used with the same dmabufs as \a buffer is found in the + * cache, return its index. Otherwise return the index of the first free V4L2 + * buffer and record its association with the dmabufs of \a buffer. + * + * \return The index of the best V4L2 buffer, or -ENOENT if no free V4L2 buffer + * is available + */ +int V4L2BufferCache::get(const FrameBuffer &buffer) +{ + bool hit = false; + int use = -1; + + for (unsigned int index = 0; index < cache_.size(); index++) { + const Entry &entry = cache_[index]; + + if (!entry.free) + continue; + + if (use < 0) + use = index; + + /* Try to find a cache hit by comparing the planes. */ + if (cache_[index] == buffer) { + hit = true; + use = index; + break; + } + } + + if (!hit) + missCounter_++; + + if (use < 0) + return -ENOENT; + + cache_[use] = Entry(false, buffer); + + return use; +} + +/** + * \brief Mark buffer \a index as free in the cache + * \param[in] index The V4L2 buffer index + */ +void V4L2BufferCache::put(unsigned int index) +{ + ASSERT(index < cache_.size()); + cache_[index].free = true; +} + +V4L2BufferCache::Entry::Entry() + : free(true) +{ +} + +V4L2BufferCache::Entry::Entry(bool free, const FrameBuffer &buffer) + : free(free) +{ + for (const FrameBuffer::Plane &plane : buffer.planes()) + planes_.emplace_back(plane); +} + +bool V4L2BufferCache::Entry::operator==(const FrameBuffer &buffer) +{ + const std::vector &planes = buffer.planes(); + + if (planes_.size() != planes.size()) + return false; + + for (unsigned int i = 0; i < planes.size(); i++) + if (planes_[i].fd != planes[i].fd.fd() || + planes_[i].length != planes[i].length) + return false; + return true; +} + /** * \class V4L2DeviceFormat * \brief The V4L2 video device image format and sizes From patchwork Sun Jan 12 01:01:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2600 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4CB6A606E8 for ; Sun, 12 Jan 2020 02:03:14 +0100 (CET) X-Halon-ID: 4cbf7716-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4cbf7716-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:10 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:58 +0100 Message-Id: <20200112010212.2609025-19-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 18/32] libcamera: v4l2_videodevice: Add FrameBuffer interface X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:15 -0000 Add a new interface in parallel with the existing Buffer implementation to also support FrameBuffer. The reason it's added in parallel is to aid in the migration from Buffer to FrameBuffer throughout libcamera. With this change discrete parts of libcamera can be migrated and tested independently. As the new interface is added in parallel there are some oddities in this change which will be undone in a follow up patch once libcamera have migrated away from the Buffer interface. - There is a nasty hack in V4L2VideoDevice::bufferAvailable(). It is needed to allow both interfaces to exist and function at the same time. The idea is if buffers are allocated using the FrameBuffer interface V4L2VideoDevice::cache_ is set and we know to call the FrameBuffer 'buffer ready' signal, and likewise if it's not to call the Buffer variant. - There is some code duplication between the two interfaces as they aim to solve the same thing in slightly different ways. As all Buffer related code is soon to be removed no effort to create code sharing between them have been made. - Some function and variables which can't be distinguished by their argument types have been given a frameBuffer prefix instead of a buffer prefix. They are clearly documented in the code and will be renamed to the correct buffer prefix when the Buffer interface is removed. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v2 - s/todo:/todo/ - Style updates - Update documentation - Remove buf.length > check in V4L2VideoDevice::dequeueFrameBuffer() - Set length for multi planer output buffers in queueBuffer() * Changes since v1 - Rename allocateBuffers() to exportBuffers() - Rename externalBuffers() to importBuffers() - Rename createBuffer() to createFrameBuffer() - Reworked createFrameBuffer() to take advantage of the FileDescriptor() - Fix up a lot of documentation and small code beautification fixes. - Merged all FrameBuffer interface additions into this patch, some where done in the really huge 27/30 patch. --- src/libcamera/include/v4l2_videodevice.h | 12 + src/libcamera/v4l2_videodevice.cpp | 313 ++++++++++++++++++++++- 2 files changed, 315 insertions(+), 10 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index 9d22754a39a75621..09967d3c6ae59044 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -184,11 +184,17 @@ public: int exportBuffers(BufferPool *pool); int importBuffers(BufferPool *pool); + int exportBuffers(unsigned int count, + std::vector> *buffers); + int importBuffers(unsigned int count); int releaseBuffers(); int queueBuffer(Buffer *buffer); std::vector> queueAllBuffers(); Signal bufferReady; + int queueBuffer(FrameBuffer *buffer); + /* todo Rename to bufferReady when the Buffer version is removed */ + Signal frameBufferReady; int streamOn(); int streamOff(); @@ -219,10 +225,13 @@ private: int requestBuffers(unsigned int count); int createPlane(BufferMemory *buffer, unsigned int index, unsigned int plane, unsigned int length); + std::unique_ptr createFrameBuffer(const struct v4l2_buffer &buf); FileDescriptor exportDmabufFd(unsigned int index, unsigned int plane); Buffer *dequeueBuffer(); void bufferAvailable(EventNotifier *notifier); + /* todo Rename to dequeueBuffer() when the Buffer version is removed */ + FrameBuffer *dequeueFrameBuffer(); V4L2Capability caps_; @@ -230,7 +239,10 @@ private: enum v4l2_memory memoryType_; BufferPool *bufferPool_; + V4L2BufferCache *cache_; std::map queuedBuffers_; + /* todo Rename to queuedBuffers_ when the Buffer version is removed */ + std::map queuedFrameBuffers_; EventNotifier *fdEvent_; }; diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 84c45dbcb85c8638..ed5b2377b806f19c 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -410,7 +410,8 @@ const std::string V4L2DeviceFormat::toString() const * \param[in] deviceNode The file-system path to the video device node */ V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode) - : V4L2Device(deviceNode), bufferPool_(nullptr), fdEvent_(nullptr) + : V4L2Device(deviceNode), bufferPool_(nullptr), cache_(nullptr), + fdEvent_(nullptr) { /* * We default to an MMAP based CAPTURE video device, however this will @@ -1075,6 +1076,94 @@ int V4L2VideoDevice::importBuffers(BufferPool *pool) return 0; } +/** + * \brief Allocate buffers from the video device + * \param[in] count Number of buffers to allocate + * \param[out] buffers Vector to store allocated buffers + * \return 0 on success or a negative error code otherwise + */ +int V4L2VideoDevice::exportBuffers(unsigned int count, + std::vector> *buffers) +{ + if (cache_) { + LOG(V4L2, Error) << "Buffers already allocated"; + return -EINVAL; + } + + memoryType_ = V4L2_MEMORY_MMAP; + + int ret = requestBuffers(count); + if (ret < 0) + return ret; + + for (unsigned i = 0; i < count; ++i) { + struct v4l2_buffer buf = {}; + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; + + buf.index = i; + buf.type = bufferType_; + buf.memory = memoryType_; + buf.length = ARRAY_SIZE(planes); + buf.m.planes = planes; + + ret = ioctl(VIDIOC_QUERYBUF, &buf); + if (ret < 0) { + LOG(V4L2, Error) + << "Unable to query buffer " << i << ": " + << strerror(-ret); + goto err_buf; + } + + std::unique_ptr buffer = createFrameBuffer(buf); + if (!buffer) { + LOG(V4L2, Error) << "Unable to create buffer"; + ret = -EINVAL; + goto err_buf; + } + + buffers->push_back(std::move(buffer)); + } + + cache_ = new V4L2BufferCache(*buffers); + + return count; + +err_buf: + requestBuffers(0); + + buffers->clear(); + + return ret; +} + +std::unique_ptr +V4L2VideoDevice::createFrameBuffer(const struct v4l2_buffer &buf) +{ + const bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type); + const unsigned int numPlanes = multiPlanar ? buf.length : 1; + + if (numPlanes == 0 || numPlanes > VIDEO_MAX_PLANES) { + LOG(V4L2, Error) << "Invalid number of planes"; + return nullptr; + } + + std::vector planes; + for (unsigned int nplane = 0; nplane < numPlanes; nplane++) { + FileDescriptor fd = exportDmabufFd(buf.index, nplane); + if (fd.fd() < 0) + return nullptr; + + FrameBuffer::Plane plane; + plane.fd = std::move(fd); + plane.length = multiPlanar ? + buf.m.planes[nplane].length : buf.length; + + planes.push_back(std::move(plane)); + } + + return utils::make_unique(std::move(planes)); +} + FileDescriptor V4L2VideoDevice::exportDmabufFd(unsigned int index, unsigned int plane) { @@ -1096,14 +1185,41 @@ FileDescriptor V4L2VideoDevice::exportDmabufFd(unsigned int index, return FileDescriptor(expbuf.fd); } +/** + * \brief Prepare the device to import \a count buffers + * \param[in] count Number of buffers to prepare to import + * \return 0 on success or a negative error code otherwise + */ +int V4L2VideoDevice::importBuffers(unsigned int count) +{ + if (cache_) { + LOG(V4L2, Error) << "Buffers already allocated"; + return -EINVAL; + } + + memoryType_ = V4L2_MEMORY_DMABUF; + + int ret = requestBuffers(count); + if (ret) + return ret; + + cache_ = new V4L2BufferCache(count); + + LOG(V4L2, Debug) << "Prepared to import " << count << " buffers"; + + return 0; +} + /** * \brief Release all internally allocated buffers */ int V4L2VideoDevice::releaseBuffers() { - LOG(V4L2, Debug) << "Releasing bufferPool"; + LOG(V4L2, Debug) << "Releasing buffers"; bufferPool_ = nullptr; + delete cache_; + cache_ = nullptr; return requestBuffers(0); } @@ -1222,6 +1338,90 @@ std::vector> V4L2VideoDevice::queueAllBuffers() return buffers; } +/** + * \brief Queue a buffer to the video device + * \param[in] buffer The buffer to be queued + * + * For capture video devices the \a buffer will be filled with data by the + * device. For output video 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. + * + * The best available V4L2 buffer is picked for \a buffer using the V4L2 buffer + * cache. + * + * \return 0 on success or a negative error code otherwise + */ +int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer) +{ + struct v4l2_plane v4l2Planes[VIDEO_MAX_PLANES] = {}; + struct v4l2_buffer buf = {}; + int ret; + + ret = cache_->get(*buffer); + if (ret < 0) + return ret; + + buf.index = ret; + buf.type = bufferType_; + buf.memory = memoryType_; + buf.field = V4L2_FIELD_NONE; + + bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type); + const std::vector &planes = buffer->planes(); + + if (buf.memory == V4L2_MEMORY_DMABUF) { + if (multiPlanar) { + for (unsigned int p = 0; p < planes.size(); ++p) + v4l2Planes[p].m.fd = planes[p].fd.fd(); + } else { + buf.m.fd = planes[0].fd.fd(); + } + } + + if (multiPlanar) { + buf.length = planes.size(); + buf.m.planes = v4l2Planes; + } + + if (V4L2_TYPE_IS_OUTPUT(buf.type)) { + const FrameMetadata &metadata = buffer->metadata(); + + if (multiPlanar) { + unsigned int nplane = 0; + for (const FrameMetadata::Plane &plane : metadata.planes) { + v4l2Planes[nplane].bytesused = plane.bytesused; + v4l2Planes[nplane].length = buffer->planes()[nplane].length; + nplane++; + } + } else { + if (metadata.planes.size()) + buf.bytesused = metadata.planes[0].bytesused; + } + + buf.sequence = metadata.sequence; + buf.timestamp.tv_sec = metadata.timestamp / 1000000000; + buf.timestamp.tv_usec = (metadata.timestamp / 1000) % 1000000; + } + + LOG(V4L2, Debug) << "Queueing buffer " << buf.index; + + ret = ioctl(VIDIOC_QBUF, &buf); + if (ret < 0) { + LOG(V4L2, Error) + << "Failed to queue buffer " << buf.index << ": " + << strerror(-ret); + return ret; + } + + if (queuedFrameBuffers_.empty()) + fdEvent_->setEnabled(true); + + queuedFrameBuffers_[buf.index] = buffer; + + return 0; +} + /** * \brief Dequeue the next available buffer from the video device * @@ -1281,17 +1481,97 @@ Buffer *V4L2VideoDevice::dequeueBuffer() * When this slot is called, a Buffer has become available from the device, and * will be emitted through the bufferReady Signal. * - * For Capture video devices the Buffer will contain valid data. - * For Output video devices the Buffer can be considered empty. + * For Capture video devices the FrameBuffer will contain valid data. + * For Output video devices the FrameBuffer can be considered empty. */ void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier) { - Buffer *buffer = dequeueBuffer(); - if (!buffer) - return; + /* + * This is a hack which allows both Buffer and FrameBuffer interfaces + * to work with the same code base. This allows different parts of + * libcamera to migrate to FrameBuffer in different patches. + * + * If we have a cache_ we know FrameBuffer is in use. + * + * \todo Remove this hack when the Buffer interface is removed. + */ + if (cache_) { + FrameBuffer *buffer = dequeueFrameBuffer(); + if (!buffer) + return; - /* Notify anyone listening to the device. */ - bufferReady.emit(buffer); + /* Notify anyone listening to the device. */ + frameBufferReady.emit(buffer); + } else { + Buffer *buffer = dequeueBuffer(); + if (!buffer) + return; + + /* Notify anyone listening to the device. */ + bufferReady.emit(buffer); + } +} + +/** + * \brief Dequeue the next available buffer from the video device + * + * This method dequeues the next available buffer from the device. If no buffer + * is available to be dequeued it will return nullptr immediately. + * + * \todo Rename to dequeueBuffer() once the FrameBuffer transition is complete + * + * \return A pointer to the dequeued buffer on success, or nullptr otherwise + */ +FrameBuffer *V4L2VideoDevice::dequeueFrameBuffer() +{ + struct v4l2_buffer buf = {}; + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; + int ret; + + buf.type = bufferType_; + buf.memory = memoryType_; + + bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type); + + if (multiPlanar) { + buf.length = VIDEO_MAX_PLANES; + buf.m.planes = planes; + } + + ret = ioctl(VIDIOC_DQBUF, &buf); + if (ret < 0) { + LOG(V4L2, Error) + << "Failed to dequeue buffer: " << strerror(-ret); + return nullptr; + } + + LOG(V4L2, Debug) << "Dequeuing buffer " << buf.index; + + cache_->put(buf.index); + + auto it = queuedFrameBuffers_.find(buf.index); + FrameBuffer *buffer = it->second; + queuedFrameBuffers_.erase(it); + + if (queuedFrameBuffers_.empty()) + fdEvent_->setEnabled(false); + + buffer->metadata_.status = buf.flags & V4L2_BUF_FLAG_ERROR + ? FrameMetadata::FrameError + : FrameMetadata::FrameSuccess; + buffer->metadata_.sequence = buf.sequence; + buffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL + + buf.timestamp.tv_usec * 1000ULL; + + buffer->metadata_.planes.clear(); + if (multiPlanar) { + for (unsigned int nplane = 0; nplane < buf.length; nplane++) + buffer->metadata_.planes.push_back({ planes[nplane].bytesused }); + } else { + buffer->metadata_.planes.push_back({ buf.bytesused }); + } + + return buffer; } /** @@ -1299,6 +1579,11 @@ void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier) * \brief A Signal emitted when a buffer completes */ +/** + * \var V4L2VideoDevice::frameBufferReady + * \brief A Signal emitted when a framebuffer completes + */ + /** * \brief Start the video stream * \return 0 on success or a negative error code otherwise @@ -1321,7 +1606,7 @@ int V4L2VideoDevice::streamOn() * \brief Stop the video stream * * Buffers that are still queued when the video stream is stopped are - * immediately dequeued with their status set to Buffer::BufferError, + * immediately dequeued with their status set to FrameMetadata::FrameCancelled, * and the bufferReady signal is emitted for them. The order in which those * buffers are dequeued is not specified. * @@ -1348,7 +1633,15 @@ int V4L2VideoDevice::streamOff() bufferReady.emit(buffer); } + for (auto it : queuedFrameBuffers_) { + FrameBuffer *buffer = it.second; + + buffer->metadata_.status = FrameMetadata::FrameCancelled; + frameBufferReady.emit(buffer); + } + queuedBuffers_.clear(); + queuedFrameBuffers_.clear(); fdEvent_->setEnabled(false); return 0; From patchwork Sun Jan 12 01:01:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2601 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 28E16606DF for ; Sun, 12 Jan 2020 02:03:15 +0100 (CET) X-Halon-ID: 4d78f8bd-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4d78f8bd-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:11 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:01:59 +0100 Message-Id: <20200112010212.2609025-20-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 19/32] test: v4l2_videodevice: Switch to FrameBuffer interface X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:15 -0000 The V4L2VideoDevice class can now operate using a FrameBuffer interface, switch all test cases to use it. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- test/v4l2_videodevice/buffer_sharing.cpp | 30 ++++++------- test/v4l2_videodevice/capture_async.cpp | 20 ++++----- test/v4l2_videodevice/request_buffers.cpp | 11 +---- test/v4l2_videodevice/stream_on_off.cpp | 6 +-- test/v4l2_videodevice/v4l2_m2mdevice.cpp | 44 ++++++++----------- test/v4l2_videodevice/v4l2_videodevice_test.h | 2 +- 6 files changed, 48 insertions(+), 65 deletions(-) diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp index fe48b2e98fdada8d..6acb06a24b47f653 100644 --- a/test/v4l2_videodevice/buffer_sharing.cpp +++ b/test/v4l2_videodevice/buffer_sharing.cpp @@ -73,16 +73,14 @@ protected: return TestFail; } - pool_.createBuffers(bufferCount); - - ret = capture_->exportBuffers(&pool_); - if (ret) { - std::cout << "Failed to export buffers" << std::endl; + ret = capture_->exportBuffers(bufferCount, &buffers_); + if (ret < 0) { + std::cout << "Failed to allocate buffers" << std::endl; return TestFail; } - ret = output_->importBuffers(&pool_); - if (ret) { + ret = output_->importBuffers(bufferCount); + if (ret < 0) { std::cout << "Failed to import buffers" << std::endl; return TestFail; } @@ -90,7 +88,7 @@ protected: return 0; } - void captureBufferReady(Buffer *buffer) + void captureBufferReady(FrameBuffer *buffer) { const FrameMetadata &metadata = buffer->metadata(); @@ -103,7 +101,7 @@ protected: framesCaptured_++; } - void outputBufferReady(Buffer *buffer) + void outputBufferReady(FrameBuffer *buffer) { const FrameMetadata &metadata = buffer->metadata(); @@ -122,13 +120,15 @@ protected: Timer timeout; int ret; - capture_->bufferReady.connect(this, &BufferSharingTest::captureBufferReady); - output_->bufferReady.connect(this, &BufferSharingTest::outputBufferReady); + capture_->frameBufferReady.connect(this, &BufferSharingTest::captureBufferReady); + output_->frameBufferReady.connect(this, &BufferSharingTest::outputBufferReady); - std::vector> buffers; - buffers = capture_->queueAllBuffers(); - if (buffers.empty()) - return TestFail; + for (const std::unique_ptr &buffer : buffers_) { + if (capture_->queueBuffer(buffer.get())) { + std::cout << "Failed to queue buffer" << std::endl; + return TestFail; + } + } ret = capture_->streamOn(); if (ret) { diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp index f62bbd837b213a0a..a57abed3bd0debc1 100644 --- a/test/v4l2_videodevice/capture_async.cpp +++ b/test/v4l2_videodevice/capture_async.cpp @@ -20,7 +20,7 @@ public: CaptureAsyncTest() : V4L2VideoDeviceTest("vimc", "Raw Capture 0"), frames(0) {} - void receiveBuffer(Buffer *buffer) + void receiveBuffer(FrameBuffer *buffer) { std::cout << "Buffer received" << std::endl; frames++; @@ -38,18 +38,18 @@ protected: Timer timeout; int ret; - pool_.createBuffers(bufferCount); - - ret = capture_->exportBuffers(&pool_); - if (ret) + ret = capture_->exportBuffers(bufferCount, &buffers_); + if (ret < 0) return TestFail; - capture_->bufferReady.connect(this, &CaptureAsyncTest::receiveBuffer); + capture_->frameBufferReady.connect(this, &CaptureAsyncTest::receiveBuffer); - std::vector> buffers; - buffers = capture_->queueAllBuffers(); - if (buffers.empty()) - return TestFail; + for (const std::unique_ptr &buffer : buffers_) { + if (capture_->queueBuffer(buffer.get())) { + std::cout << "Failed to queue buffer" << std::endl; + return TestFail; + } + } ret = capture_->streamOn(); if (ret) diff --git a/test/v4l2_videodevice/request_buffers.cpp b/test/v4l2_videodevice/request_buffers.cpp index c4aedf7b3cd61e80..1dd65b05da430e63 100644 --- a/test/v4l2_videodevice/request_buffers.cpp +++ b/test/v4l2_videodevice/request_buffers.cpp @@ -16,17 +16,10 @@ public: protected: int run() { - /* - * TODO: - * Test invalid requests - * Test different buffer allocations - */ const unsigned int bufferCount = 8; - pool_.createBuffers(bufferCount); - - int ret = capture_->exportBuffers(&pool_); - if (ret) + int ret = capture_->exportBuffers(bufferCount, &buffers_); + if (ret != bufferCount) return TestFail; return TestPass; diff --git a/test/v4l2_videodevice/stream_on_off.cpp b/test/v4l2_videodevice/stream_on_off.cpp index 7664adc4c1f07046..552df0963633ae4b 100644 --- a/test/v4l2_videodevice/stream_on_off.cpp +++ b/test/v4l2_videodevice/stream_on_off.cpp @@ -17,10 +17,8 @@ protected: { const unsigned int bufferCount = 8; - pool_.createBuffers(bufferCount); - - int ret = capture_->exportBuffers(&pool_); - if (ret) + int ret = capture_->exportBuffers(bufferCount, &buffers_); + if (ret < 0) return TestFail; ret = capture_->streamOn(); diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp index 442bcac5dc49cc59..43b99c4f7ea9bf26 100644 --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp @@ -29,7 +29,7 @@ public: { } - void outputBufferComplete(Buffer *buffer) + void outputBufferComplete(FrameBuffer *buffer) { cout << "Received output buffer" << endl; @@ -39,7 +39,7 @@ public: vim2m_->output()->queueBuffer(buffer); } - void receiveCaptureBuffer(Buffer *buffer) + void receiveCaptureBuffer(FrameBuffer *buffer) { cout << "Received capture buffer" << endl; @@ -112,39 +112,31 @@ protected: return TestFail; } - capturePool_.createBuffers(bufferCount); - outputPool_.createBuffers(bufferCount); - - ret = capture->exportBuffers(&capturePool_); - if (ret) { + ret = capture->exportBuffers(bufferCount, &captureBuffers_); + if (ret < 0) { cerr << "Failed to export Capture Buffers" << endl; return TestFail; } - ret = output->exportBuffers(&outputPool_); - if (ret) { + ret = output->exportBuffers(bufferCount, &outputBuffers_); + if (ret < 0) { cerr << "Failed to export Output Buffers" << endl; return TestFail; } - capture->bufferReady.connect(this, &V4L2M2MDeviceTest::receiveCaptureBuffer); - output->bufferReady.connect(this, &V4L2M2MDeviceTest::outputBufferComplete); + capture->frameBufferReady.connect(this, &V4L2M2MDeviceTest::receiveCaptureBuffer); + output->frameBufferReady.connect(this, &V4L2M2MDeviceTest::outputBufferComplete); - std::vector> captureBuffers; - captureBuffers = capture->queueAllBuffers(); - if (captureBuffers.empty()) { - cerr << "Failed to queue all Capture Buffers" << endl; - return TestFail; + for (const std::unique_ptr &buffer : captureBuffers_) { + if (capture->queueBuffer(buffer.get())) { + std::cout << "Failed to queue capture buffer" << std::endl; + return TestFail; + } } - /* We can't "queueAllBuffers()" on an output device, so we do it manually */ - std::vector> outputBuffers; - for (unsigned int i = 0; i < outputPool_.count(); ++i) { - Buffer *buffer = new Buffer(i); - outputBuffers.emplace_back(buffer); - ret = output->queueBuffer(buffer); - if (ret) { - cerr << "Failed to queue output buffer" << i << endl; + for (const std::unique_ptr &buffer : outputBuffers_) { + if (output->queueBuffer(buffer.get())) { + std::cout << "Failed to queue output buffer" << std::endl; return TestFail; } } @@ -202,8 +194,8 @@ private: std::shared_ptr media_; V4L2M2MDevice *vim2m_; - BufferPool capturePool_; - BufferPool outputPool_; + std::vector> captureBuffers_; + std::vector> outputBuffers_; unsigned int outputFrames_; unsigned int captureFrames_; diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h index 34dd231c6d9d108d..9acaceb84fe0a12f 100644 --- a/test/v4l2_videodevice/v4l2_videodevice_test.h +++ b/test/v4l2_videodevice/v4l2_videodevice_test.h @@ -41,7 +41,7 @@ protected: CameraSensor *sensor_; V4L2Subdevice *debayer_; V4L2VideoDevice *capture_; - BufferPool pool_; + std::vector> buffers_; }; #endif /* __LIBCAMERA_V4L2_DEVICE_TEST_H_ */ From patchwork Sun Jan 12 01:02:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2602 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4EF19606E8 for ; Sun, 12 Jan 2020 02:03:16 +0100 (CET) X-Halon-ID: 4dff245a-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4dff245a-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:12 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:00 +0100 Message-Id: <20200112010212.2609025-21-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 20/32] test: camera: buffer_import: Update to FrameBuffer restrictions X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:16 -0000 With the FrameBuffer interface the V4L2 buffer indexes are not visible outside the V4L2VideoDevice so it's not possible to to test that the expected indexes are used when using external buffers (allocated directly from a V4L2 video device) with a Camera. Rewrite the test to this limitation. The idea of the test stays the same, test that buffers allocated from a V4L2 video device (vivid) can be imported and used on a Camera (vimc). As an added bonus the rewrite makes use of the FrameBuffer interface for the allocation of buffers at the source (vivid) and imports them to the Camera which still uses the Buffer interface internally. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- test/camera/buffer_import.cpp | 389 ++++++++++------------------------ 1 file changed, 111 insertions(+), 278 deletions(-) diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index 3ba6ce9690f29329..327db7912c09ed2d 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -22,19 +22,32 @@ using namespace libcamera; -/* Keep SINK_BUFFER_COUNT > CAMERA_BUFFER_COUNT + 1 */ -static constexpr unsigned int SINK_BUFFER_COUNT = 8; -static constexpr unsigned int CAMERA_BUFFER_COUNT = 4; +namespace { -class FrameSink +/* A provider of external buffers, suitable for import by a Camera. */ +class BufferSource { public: - FrameSink() + BufferSource() : video_(nullptr) { } - int init() + ~BufferSource() + { + if (video_) { + video_->releaseBuffers(); + video_->close(); + } + + delete video_; + video_ = nullptr; + + if (media_) + media_->release(); + } + + int allocate(const StreamConfiguration &config) { int ret; @@ -72,187 +85,30 @@ public: return TestFail; /* Configure the format. */ - ret = video_->getFormat(&format_); + V4L2DeviceFormat format; + ret = video_->getFormat(&format); if (ret) { std::cout << "Failed to get format on output device" << std::endl; return ret; } - format_.size.width = 1920; - format_.size.height = 1080; - format_.fourcc = V4L2_PIX_FMT_RGB24; - format_.planesCount = 1; - format_.planes[0].size = 1920 * 1080 * 3; - format_.planes[0].bpl = 1920 * 3; - - if (video_->setFormat(&format_)) { - cleanup(); - return TestFail; - } - - /* Export the buffers to a pool. */ - pool_.createBuffers(SINK_BUFFER_COUNT); - ret = video_->exportBuffers(&pool_); - if (ret) { - std::cout << "Failed to export buffers" << std::endl; - cleanup(); + format.size = config.size; + format.fourcc = V4L2VideoDevice::toV4L2Fourcc(config.pixelFormat, false); + if (video_->setFormat(&format)) return TestFail; - } - - /* Only use the first CAMERA_BUFFER_COUNT buffers to start with. */ - availableBuffers_.resize(CAMERA_BUFFER_COUNT); - std::iota(availableBuffers_.begin(), availableBuffers_.end(), 0); - - /* Connect the buffer ready signal. */ - video_->bufferReady.connect(this, &FrameSink::bufferComplete); - - return TestPass; - } - - void cleanup() - { - if (video_) { - video_->streamOff(); - video_->releaseBuffers(); - video_->close(); - - delete video_; - video_ = nullptr; - } - - if (media_) - media_->release(); - } - - int start() - { - requestsCount_ = 0; - done_ = false; - - int ret = video_->streamOn(); - if (ret < 0) - return ret; - - /* Queue all the initial requests. */ - for (unsigned int index = 0; index < CAMERA_BUFFER_COUNT; ++index) - queueRequest(index); - return 0; + return video_->exportBuffers(config.bufferCount, &buffers_); } - int stop() + const std::vector> &buffers() { - return video_->streamOff(); + return buffers_; } - void requestComplete(uint64_t cookie, const Buffer *metadata) - { - unsigned int index = cookie; - - Buffer *buffer = new Buffer(index, metadata); - int ret = video_->queueBuffer(buffer); - if (ret < 0) - std::cout << "Failed to queue buffer to sink" << std::endl; - } - - bool done() const { return done_; } - - PixelFormat format() const - { - return video_->toPixelFormat(format_.fourcc); - } - - const Size &size() const - { - return format_.size; - } - - Signal requestReady; - private: - void queueRequest(unsigned int index) - { - auto it = std::find(availableBuffers_.begin(), - availableBuffers_.end(), index); - availableBuffers_.erase(it); - - uint64_t cookie = index; - BufferMemory &mem = pool_.buffers()[index]; - int dmabuf = mem.planes()[0].fd.fd(); - - requestReady.emit(cookie, dmabuf); - - requestsCount_++; - } - - void bufferComplete(Buffer *buffer) - { - availableBuffers_.push_back(buffer->index()); - - /* - * Pick the buffer for the next request among the available - * buffers. - * - * For the first 20 frames, select the buffer that has just - * completed to keep the mapping of dmabuf fds to buffers - * unchanged in the camera. - * - * For the next 20 frames, cycle randomly over the available - * buffers. The mapping should still be kept unchanged, as the - * camera should map using the cached fds. - * - * For the last 20 frames, cycles through all buffers, which - * should trash the mappings. - */ - unsigned int index = buffer->index(); - delete buffer; - - std::cout << "Completed buffer, request=" << requestsCount_ - << ", available buffers=" << availableBuffers_.size() - << std::endl; - - if (requestsCount_ >= 60) { - if (availableBuffers_.size() == SINK_BUFFER_COUNT) - done_ = true; - return; - } - - if (requestsCount_ == 40) { - /* Add the remaining of the buffers. */ - for (unsigned int i = CAMERA_BUFFER_COUNT; - i < SINK_BUFFER_COUNT; ++i) - availableBuffers_.push_back(i); - } - - if (requestsCount_ >= 20) { - /* - * Wait until we have enough buffers to make this - * meaningful. Preferably half of the camera buffers, - * but no less than 2 in any case. - */ - const unsigned int min_pool_size = - std::min(CAMERA_BUFFER_COUNT / 2, 2U); - if (availableBuffers_.size() < min_pool_size) - return; - - /* Pick a buffer at random. */ - unsigned int pos = random_() % availableBuffers_.size(); - index = availableBuffers_[pos]; - } - - queueRequest(index); - } - std::shared_ptr media_; V4L2VideoDevice *video_; - BufferPool pool_; - V4L2DeviceFormat format_; - - unsigned int requestsCount_; - std::vector availableBuffers_; - std::random_device random_; - - bool done_; + std::vector> buffers_; }; class BufferImportTest : public CameraTest, public Test @@ -263,175 +119,152 @@ public: { } - void queueRequest(uint64_t cookie, int dmabuf) - { - Request *request = camera_->createRequest(cookie); - - std::unique_ptr buffer = stream_->createBuffer({ dmabuf, -1, -1 }); - request->addBuffer(stream_, move(buffer)); - camera_->queueRequest(request); - } - protected: void bufferComplete(Request *request, Buffer *buffer) { if (buffer->metadata().status != FrameMetadata::FrameSuccess) return; - unsigned int index = buffer->index(); - int dmabuf = buffer->dmabufs()[0]; - - /* Record dmabuf to index remappings. */ - bool remapped = false; - if (bufferMappings_.find(index) != bufferMappings_.end()) { - if (bufferMappings_[index] != dmabuf) - remapped = true; - } + completeBuffersCount_++; + } - std::cout << "Completed request " << framesCaptured_ - << ": dmabuf fd " << dmabuf - << " -> index " << index - << " (" << (remapped ? 'R' : '-') << ")" - << std::endl; + void requestComplete(Request *request) + { + if (request->status() != Request::RequestComplete) + return; - if (remapped) - bufferRemappings_.push_back(framesCaptured_); + const std::map &buffers = request->buffers(); - bufferMappings_[index] = dmabuf; - framesCaptured_++; + completeRequestsCount_++; - sink_.requestComplete(request->cookie(), buffer); + /* Create a new request. */ + Stream *stream = buffers.begin()->first; + int dmabuf = buffers.begin()->second->dmabufs()[0]; + std::unique_ptr buffer = stream->createBuffer({ dmabuf, -1, -1 }); - if (framesCaptured_ == 60) - sink_.stop(); + request = camera_->createRequest(); + request->addBuffer(stream, std::move(buffer)); + camera_->queueRequest(request); } - int initCamera() + int init() override { - if (camera_->acquire()) { - std::cout << "Failed to acquire the camera" << std::endl; - return TestFail; - } + if (status_ != TestPass) + return status_; - /* - * Configure the Stream to work with externally allocated - * buffers by setting the memoryType to ExternalMemory. - */ - std::unique_ptr config; - config = camera_->generateConfiguration({ StreamRole::VideoRecording }); - if (!config || config->size() != 1) { - std::cout << "Failed to generate configuration" << std::endl; + config_ = camera_->generateConfiguration({ StreamRole::VideoRecording }); + if (!config_ || config_->size() != 1) { + std::cout << "Failed to generate default configuration" << std::endl; return TestFail; } - StreamConfiguration &cfg = config->at(0); - cfg.size = sink_.size(); - cfg.pixelFormat = sink_.format(); - cfg.bufferCount = CAMERA_BUFFER_COUNT; + StreamConfiguration &cfg = config_->at(0); cfg.memoryType = ExternalMemory; - if (camera_->configure(config.get())) { - std::cout << "Failed to set configuration" << std::endl; + return TestPass; + } + + int run() override + { + StreamConfiguration &cfg = config_->at(0); + + if (camera_->acquire()) { + std::cout << "Failed to acquire the camera" << std::endl; return TestFail; } - stream_ = cfg.stream(); + if (camera_->configure(config_.get())) { + std::cout << "Failed to set default configuration" << std::endl; + return TestFail; + } - /* Allocate buffers. */ if (camera_->allocateBuffers()) { std::cout << "Failed to allocate buffers" << std::endl; return TestFail; } - /* Connect the buffer completed signal. */ - camera_->bufferCompleted.connect(this, &BufferImportTest::bufferComplete); + Stream *stream = cfg.stream(); - return TestPass; - } + BufferSource source; + int ret = source.allocate(cfg); + if (ret < 0) + return TestFail; - int init() - { - if (status_ != TestPass) - return status_; + std::vector requests; + for (const std::unique_ptr &framebuffer : source.buffers()) { + int dmabuf = framebuffer->planes()[0].fd.fd(); - int ret = sink_.init(); - if (ret != TestPass) { - cleanup(); - return ret; - } + Request *request = camera_->createRequest(); + if (!request) { + std::cout << "Failed to create request" << std::endl; + return TestFail; + } - ret = initCamera(); - if (ret != TestPass) { - cleanup(); - return ret; - } + std::unique_ptr buffer = stream->createBuffer({ dmabuf, -1, -1 }); + if (request->addBuffer(stream, std::move(buffer))) { + std::cout << "Failed to associating buffer with request" << std::endl; + return TestFail; + } - sink_.requestReady.connect(this, &BufferImportTest::queueRequest); - return TestPass; - } + requests.push_back(request); + } - int run() - { - int ret; + completeRequestsCount_ = 0; + completeBuffersCount_ = 0; - framesCaptured_ = 0; + camera_->bufferCompleted.connect(this, &BufferImportTest::bufferComplete); + camera_->requestCompleted.connect(this, &BufferImportTest::requestComplete); if (camera_->start()) { std::cout << "Failed to start camera" << std::endl; return TestFail; } - ret = sink_.start(); - if (ret < 0) { - std::cout << "Failed to start sink" << std::endl; - return TestFail; + for (Request *request : requests) { + if (camera_->queueRequest(request)) { + std::cout << "Failed to queue request" << std::endl; + return TestFail; + } } EventDispatcher *dispatcher = cm_->eventDispatcher(); Timer timer; - timer.start(5000); - while (timer.isRunning() && !sink_.done()) + timer.start(1000); + while (timer.isRunning()) dispatcher->processEvents(); - std::cout << framesCaptured_ << " frames captured, " - << bufferRemappings_.size() << " buffers remapped" - << std::endl; + if (completeRequestsCount_ <= cfg.bufferCount * 2) { + std::cout << "Failed to capture enough frames (got " + << completeRequestsCount_ << " expected at least " + << cfg.bufferCount * 2 << ")" << std::endl; + return TestFail; + } - if (framesCaptured_ < 60) { - std::cout << "Too few frames captured" << std::endl; + if (completeRequestsCount_ != completeBuffersCount_) { + std::cout << "Number of completed buffers and requests differ" << std::endl; return TestFail; } - if (bufferRemappings_.empty()) { - std::cout << "No buffer remappings" << std::endl; + if (camera_->stop()) { + std::cout << "Failed to stop camera" << std::endl; return TestFail; } - if (bufferRemappings_[0] < 40) { - std::cout << "Early buffer remapping" << std::endl; + if (camera_->freeBuffers()) { + std::cout << "Failed to free buffers" << std::endl; return TestFail; } return TestPass; } - void cleanup() - { - sink_.cleanup(); - - camera_->stop(); - camera_->freeBuffers(); - } - private: - Stream *stream_; - - std::map bufferMappings_; - std::vector bufferRemappings_; - unsigned int framesCaptured_; - - FrameSink sink_; + unsigned int completeBuffersCount_; + unsigned int completeRequestsCount_; + std::unique_ptr config_; }; +} /* namespace */ + TEST_REGISTER(BufferImportTest); From patchwork Sun Jan 12 01:02:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2603 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0888F606F1 for ; Sun, 12 Jan 2020 02:03:17 +0100 (CET) X-Halon-ID: 4ea7f0f4-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4ea7f0f4-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:13 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:01 +0100 Message-Id: <20200112010212.2609025-22-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 21/32] libcamera: pipeline: rkisp1: Destroy frame information before completing request X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:17 -0000 It's common for applications to create and queue a new request in a previous request completion handler. When the new request gets queued to the RkISP1 pipeline handler it tries to find a parameters and statistic buffer to be used with the request. The problem is if the pipeline depth is already filled there are no internal buffers free to be used by the new request. This was solved by allocation one more parameters and statistic buffer then the pipeline depth, this is waste full. Instead free the resources of the request that has completed before it is signaled to the application, this way if the pipeline depth is full it can reuse the internal resources and the wasteful allocation can be removed. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v2 - Rewrote commit message. - Actually remove the wasteful allocation of extra buffers in this commit instead of depending on the switch to FrameBuffer to do it for us. --- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 979b670e4cb75512..607ff85539a22324 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -671,14 +671,14 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, if (ret) return ret; - paramPool_.createBuffers(stream->configuration().bufferCount + 1); + paramPool_.createBuffers(stream->configuration().bufferCount); ret = param_->exportBuffers(¶mPool_); if (ret) { video_->releaseBuffers(); return ret; } - statPool_.createBuffers(stream->configuration().bufferCount + 1); + statPool_.createBuffers(stream->configuration().bufferCount); ret = stat_->exportBuffers(&statPool_); if (ret) { param_->releaseBuffers(); @@ -686,13 +686,13 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, return ret; } - for (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) { + for (unsigned int i = 0; i < stream->configuration().bufferCount; i++) { data->ipaBuffers_.push_back({ .id = RKISP1_PARAM_BASE | i, .planes = paramPool_.buffers()[i].planes() }); paramBuffers_.push(new Buffer(i)); } - for (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) { + for (unsigned int i = 0; i < stream->configuration().bufferCount; i++) { data->ipaBuffers_.push_back({ .id = RKISP1_STAT_BASE | i, .planes = statPool_.buffers()[i].planes() }); statBuffers_.push(new Buffer(i)); @@ -981,9 +981,9 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request) if (!info->paramDequeued) return; - completeRequest(activeCamera_, request); - data->frameInfo_.destroy(info->frame); + + completeRequest(activeCamera_, request); } void PipelineHandlerRkISP1::bufferReady(Buffer *buffer) From patchwork Sun Jan 12 01:02:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2604 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DAFC1606E8 for ; Sun, 12 Jan 2020 02:03:18 +0100 (CET) X-Halon-ID: 4f13d40c-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 4f13d40c-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:15 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:02 +0100 Message-Id: <20200112010212.2609025-23-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 22/32] libcamera: pipeline: rkisp1: Switch to FrameBuffer interface for stat and param X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:19 -0000 The V4L2VideoDevice class can now operate using a FrameBuffer interface, switch the RkISP1 statistics and parameters buffer to use it. We can not convert the application-facing buffers yet. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v2 - Rename {param,stat}BuffersMemory to {param,stat}Buffers_ - Rename {param,stat}Buffers_ to available{Param,Stat}Buffers_ - s/err:/error:/ - Beutification of small code blocks --- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 132 ++++++++++++----------- 1 file changed, 72 insertions(+), 60 deletions(-) diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 607ff85539a22324..dbc5df801f30e76b 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -32,9 +32,6 @@ #include "v4l2_subdevice.h" #include "v4l2_videodevice.h" -#define RKISP1_PARAM_BASE 0x100 -#define RKISP1_STAT_BASE 0x200 - namespace libcamera { LOG_DEFINE_CATEGORY(RkISP1) @@ -52,8 +49,8 @@ struct RkISP1FrameInfo { unsigned int frame; Request *request; - Buffer *paramBuffer; - Buffer *statBuffer; + FrameBuffer *paramBuffer; + FrameBuffer *statBuffer; Buffer *videoBuffer; bool paramFilled; @@ -71,6 +68,7 @@ public: RkISP1FrameInfo *find(unsigned int frame); RkISP1FrameInfo *find(Buffer *buffer); + RkISP1FrameInfo *find(FrameBuffer *buffer); RkISP1FrameInfo *find(Request *request); private: @@ -203,8 +201,8 @@ private: int createCamera(MediaEntity *sensor); void tryCompleteRequest(Request *request); void bufferReady(Buffer *buffer); - void paramReady(Buffer *buffer); - void statReady(Buffer *buffer); + void paramReady(FrameBuffer *buffer); + void statReady(FrameBuffer *buffer); MediaDevice *media_; V4L2Subdevice *dphy_; @@ -213,11 +211,10 @@ private: V4L2VideoDevice *param_; V4L2VideoDevice *stat_; - BufferPool paramPool_; - BufferPool statPool_; - - std::queue paramBuffers_; - std::queue statBuffers_; + std::vector> paramBuffers_; + std::vector> statBuffers_; + std::queue availableParamBuffers_; + std::queue availableStatBuffers_; Camera *activeCamera_; }; @@ -229,17 +226,17 @@ RkISP1Frames::RkISP1Frames(PipelineHandler *pipe) RkISP1FrameInfo *RkISP1Frames::create(unsigned int frame, Request *request, Stream *stream) { - if (pipe_->paramBuffers_.empty()) { + if (pipe_->availableParamBuffers_.empty()) { LOG(RkISP1, Error) << "Parameters buffer underrun"; return nullptr; } - Buffer *paramBuffer = pipe_->paramBuffers_.front(); + FrameBuffer *paramBuffer = pipe_->availableParamBuffers_.front(); - if (pipe_->statBuffers_.empty()) { + if (pipe_->availableStatBuffers_.empty()) { LOG(RkISP1, Error) << "Statisitc buffer underrun"; return nullptr; } - Buffer *statBuffer = pipe_->statBuffers_.front(); + FrameBuffer *statBuffer = pipe_->availableStatBuffers_.front(); Buffer *videoBuffer = request->findBuffer(stream); if (!videoBuffer) { @@ -248,8 +245,8 @@ RkISP1FrameInfo *RkISP1Frames::create(unsigned int frame, Request *request, Stre return nullptr; } - pipe_->paramBuffers_.pop(); - pipe_->statBuffers_.pop(); + pipe_->availableParamBuffers_.pop(); + pipe_->availableStatBuffers_.pop(); RkISP1FrameInfo *info = new RkISP1FrameInfo; @@ -273,8 +270,8 @@ int RkISP1Frames::destroy(unsigned int frame) if (!info) return -ENOENT; - pipe_->paramBuffers_.push(info->paramBuffer); - pipe_->statBuffers_.push(info->statBuffer); + pipe_->availableParamBuffers_.push(info->paramBuffer); + pipe_->availableStatBuffers_.push(info->statBuffer); frameInfo_.erase(info->frame); @@ -299,9 +296,21 @@ RkISP1FrameInfo *RkISP1Frames::find(Buffer *buffer) for (auto &itInfo : frameInfo_) { RkISP1FrameInfo *info = itInfo.second; + if (info->videoBuffer == buffer) + return info; + } + + LOG(RkISP1, Error) << "Can't locate info from buffer"; + return nullptr; +} + +RkISP1FrameInfo *RkISP1Frames::find(FrameBuffer *buffer) +{ + for (auto &itInfo : frameInfo_) { + RkISP1FrameInfo *info = itInfo.second; + if (info->paramBuffer == buffer || - info->statBuffer == buffer || - info->videoBuffer == buffer) + info->statBuffer == buffer) return info; } @@ -661,6 +670,7 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, { RkISP1CameraData *data = cameraData(camera); Stream *stream = *streams.begin(); + unsigned int count = 1; int ret; if (stream->memoryType() == InternalMemory) @@ -671,35 +681,41 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, if (ret) return ret; - paramPool_.createBuffers(stream->configuration().bufferCount); - ret = param_->exportBuffers(¶mPool_); - if (ret) { - video_->releaseBuffers(); - return ret; - } + unsigned int maxBuffers = 0; + for (const Stream *s : camera->streams()) + maxBuffers = std::max(maxBuffers, s->configuration().bufferCount); - statPool_.createBuffers(stream->configuration().bufferCount); - ret = stat_->exportBuffers(&statPool_); - if (ret) { - param_->releaseBuffers(); - video_->releaseBuffers(); - return ret; - } + ret = param_->exportBuffers(maxBuffers, ¶mBuffers_); + if (ret < 0) + goto error; + + ret = stat_->exportBuffers(maxBuffers, &statBuffers_); + if (ret < 0) + goto error; - for (unsigned int i = 0; i < stream->configuration().bufferCount; i++) { - data->ipaBuffers_.push_back({ .id = RKISP1_PARAM_BASE | i, - .planes = paramPool_.buffers()[i].planes() }); - paramBuffers_.push(new Buffer(i)); + for (std::unique_ptr &buffer : paramBuffers_) { + buffer->setCookie(count++); + data->ipaBuffers_.push_back({ .id = buffer->cookie(), + .planes = buffer->planes() }); + availableParamBuffers_.push(buffer.get()); } - for (unsigned int i = 0; i < stream->configuration().bufferCount; i++) { - data->ipaBuffers_.push_back({ .id = RKISP1_STAT_BASE | i, - .planes = statPool_.buffers()[i].planes() }); - statBuffers_.push(new Buffer(i)); + for (std::unique_ptr &buffer : statBuffers_) { + buffer->setCookie(count++); + data->ipaBuffers_.push_back({ .id = buffer->cookie(), + .planes = buffer->planes() }); + availableStatBuffers_.push(buffer.get()); } data->ipa_->mapBuffers(data->ipaBuffers_); + return 0; + +error: + paramBuffers_.clear(); + statBuffers_.clear(); + video_->releaseBuffers(); + return ret; } @@ -708,15 +724,14 @@ int PipelineHandlerRkISP1::freeBuffers(Camera *camera, { RkISP1CameraData *data = cameraData(camera); - while (!statBuffers_.empty()) { - delete statBuffers_.front(); - statBuffers_.pop(); - } + while (!availableStatBuffers_.empty()) + availableStatBuffers_.pop(); - while (!paramBuffers_.empty()) { - delete paramBuffers_.front(); - paramBuffers_.pop(); - } + while (!availableParamBuffers_.empty()) + availableParamBuffers_.pop(); + + paramBuffers_.clear(); + statBuffers_.clear(); std::vector ids; for (IPABuffer &ipabuf : data->ipaBuffers_) @@ -823,7 +838,7 @@ int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, IPAOperationData op; op.operation = RKISP1_IPA_EVENT_QUEUE_REQUEST; - op.data = { data->frame_, RKISP1_PARAM_BASE | info->paramBuffer->index() }; + op.data = { data->frame_, info->paramBuffer->cookie() }; op.controls = { request->controls() }; data->ipa_->processEvent(op); @@ -938,8 +953,8 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator) return false; video_->bufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady); - stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady); - param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady); + stat_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::statReady); + param_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::paramReady); /* Configure default links. */ if (initLinks() < 0) { @@ -1001,7 +1016,7 @@ void PipelineHandlerRkISP1::bufferReady(Buffer *buffer) tryCompleteRequest(request); } -void PipelineHandlerRkISP1::paramReady(Buffer *buffer) +void PipelineHandlerRkISP1::paramReady(FrameBuffer *buffer) { ASSERT(activeCamera_); RkISP1CameraData *data = cameraData(activeCamera_); @@ -1012,7 +1027,7 @@ void PipelineHandlerRkISP1::paramReady(Buffer *buffer) tryCompleteRequest(info->request); } -void PipelineHandlerRkISP1::statReady(Buffer *buffer) +void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer) { ASSERT(activeCamera_); RkISP1CameraData *data = cameraData(activeCamera_); @@ -1021,12 +1036,9 @@ void PipelineHandlerRkISP1::statReady(Buffer *buffer) if (!info) return; - unsigned int frame = info->frame; - unsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index(); - IPAOperationData op; op.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER; - op.data = { frame, statid }; + op.data = { info->frame, info->statBuffer->cookie() }; data->ipa_->processEvent(op); } From patchwork Sun Jan 12 01:02:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2605 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CD3BC606E7 for ; Sun, 12 Jan 2020 02:03:19 +0100 (CET) X-Halon-ID: 50344b0e-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 50344b0e-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:15 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:03 +0100 Message-Id: <20200112010212.2609025-24-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 23/32] libcamera: pipeline: ipu3: Switch to FrameBuffer interface for cio2 and stat X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:20 -0000 The V4L2VideoDevice class can now operate using a FrameBuffer interface, switch the IPU3 CIO2 and statistics buffer to use it. We can not convert the application-facing buffers yet. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v2 - Fix spelling in commit message - Update a comment in the code path touch by this patch --- src/libcamera/pipeline/ipu3/ipu3.cpp | 107 ++++++++++++--------------- 1 file changed, 46 insertions(+), 61 deletions(-) diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 34fc792977d151be..7c4539d488bd2d26 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -45,6 +45,7 @@ public: unsigned int pad; std::string name; BufferPool *pool; + std::vector> buffers; }; ImgUDevice() @@ -70,7 +71,6 @@ public: int configureOutput(ImgUOutput *output, const StreamConfiguration &cfg); - int importInputBuffers(BufferPool *pool); int importOutputBuffers(ImgUOutput *output, BufferPool *pool); int exportOutputBuffers(ImgUOutput *output, BufferPool *pool); void freeBuffers(); @@ -95,7 +95,6 @@ public: /* \todo Add param video device for 3A tuning */ BufferPool vfPool_; - BufferPool statPool_; BufferPool outPool_; }; @@ -120,10 +119,10 @@ public: int configure(const Size &size, V4L2DeviceFormat *outputFormat); - BufferPool *exportBuffers(); + int allocateBuffers(); void freeBuffers(); - int start(std::vector> *buffers); + int start(); int stop(); static int mediaBusToFormat(unsigned int code); @@ -132,7 +131,8 @@ public: V4L2Subdevice *csi2_; CameraSensor *sensor_; - BufferPool pool_; +private: + std::vector> buffers_; }; class IPU3Stream : public Stream @@ -157,16 +157,14 @@ public: } void imguOutputBufferReady(Buffer *buffer); - void imguInputBufferReady(Buffer *buffer); - void cio2BufferReady(Buffer *buffer); + void imguInputBufferReady(FrameBuffer *buffer); + void cio2BufferReady(FrameBuffer *buffer); CIO2Device cio2_; ImgUDevice *imgu_; IPU3Stream outStream_; IPU3Stream vfStream_; - - std::vector> rawBuffers_; }; class IPU3CameraConfiguration : public CameraConfiguration @@ -638,24 +636,28 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera, int ret; /* Share buffers between CIO2 output and ImgU input. */ - BufferPool *pool = cio2->exportBuffers(); - if (!pool) - return -ENOMEM; + ret = cio2->allocateBuffers(); + if (ret < 0) + return ret; - ret = imgu->importInputBuffers(pool); - if (ret) + bufferCount = ret; + + ret = imgu->input_->importBuffers(bufferCount); + if (ret) { + LOG(IPU3, Error) << "Failed to import ImgU input buffers"; goto error; + } /* - * Use for the stat's internal pool the same number of buffer as - * for the input pool. + * Use for the stat's internal pool the same number of buffers as for + * the input pool. * \todo To be revised when we'll actually use the stat node. */ - bufferCount = pool->count(); - imgu->stat_.pool->createBuffers(bufferCount); - ret = imgu->exportOutputBuffers(&imgu->stat_, imgu->stat_.pool); - if (ret) + ret = imgu->stat_.dev->exportBuffers(bufferCount, &imgu->stat_.buffers); + if (ret < 0) { + LOG(IPU3, Error) << "Failed to allocate ImgU stat buffers"; goto error; + } /* Allocate buffers for each active stream. */ for (Stream *s : streams) { @@ -722,7 +724,7 @@ int PipelineHandlerIPU3::start(Camera *camera) * Start the ImgU video devices, buffers will be queued to the * ImgU output and viewfinder when requests will be queued. */ - ret = cio2->start(&data->rawBuffers_); + ret = cio2->start(); if (ret) goto error; @@ -738,7 +740,6 @@ int PipelineHandlerIPU3::start(Camera *camera) error: LOG(IPU3, Error) << "Failed to start camera " << camera->name(); - data->rawBuffers_.clear(); return ret; } @@ -752,8 +753,6 @@ void PipelineHandlerIPU3::stop(Camera *camera) if (ret) LOG(IPU3, Warning) << "Failed to stop camera " << camera->name(); - - data->rawBuffers_.clear(); } int PipelineHandlerIPU3::queueRequestDevice(Camera *camera, Request *request) @@ -885,9 +884,9 @@ int PipelineHandlerIPU3::registerCameras() * associated ImgU input where they get processed and * returned through the ImgU main and secondary outputs. */ - data->cio2_.output_->bufferReady.connect(data.get(), + data->cio2_.output_->frameBufferReady.connect(data.get(), &IPU3CameraData::cio2BufferReady); - data->imgu_->input_->bufferReady.connect(data.get(), + data->imgu_->input_->frameBufferReady.connect(data.get(), &IPU3CameraData::imguInputBufferReady); data->imgu_->output_.dev->bufferReady.connect(data.get(), &IPU3CameraData::imguOutputBufferReady); @@ -925,7 +924,7 @@ int PipelineHandlerIPU3::registerCameras() * Buffers completed from the ImgU input are immediately queued back to the * CIO2 unit to continue frame capture. */ -void IPU3CameraData::imguInputBufferReady(Buffer *buffer) +void IPU3CameraData::imguInputBufferReady(FrameBuffer *buffer) { /* \todo Handle buffer failures when state is set to BufferError. */ if (buffer->metadata().status == FrameMetadata::FrameCancelled) @@ -959,7 +958,7 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer) * Buffers completed from the CIO2 are immediately queued to the ImgU unit * for further processing. */ -void IPU3CameraData::cio2BufferReady(Buffer *buffer) +void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer) { /* \todo Handle buffer failures when state is set to BufferError. */ if (buffer->metadata().status == FrameMetadata::FrameCancelled) @@ -1034,7 +1033,6 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index) stat_.pad = PAD_STAT; stat_.name = "stat"; - stat_.pool = &statPool_; return 0; } @@ -1134,22 +1132,6 @@ int ImgUDevice::configureOutput(ImgUOutput *output, return 0; } -/** - * \brief Import buffers from \a pool into the ImgU input - * \param[in] pool The buffer pool to import - * \return 0 on success or a negative error code otherwise - */ -int ImgUDevice::importInputBuffers(BufferPool *pool) -{ - int ret = input_->importBuffers(pool); - if (ret) { - LOG(IPU3, Error) << "Failed to import ImgU input buffers"; - return ret; - } - - return 0; -} - /** * \brief Export buffers from \a output to the provided \a pool * \param[in] output The ImgU output device @@ -1448,37 +1430,40 @@ int CIO2Device::configure(const Size &size, } /** - * \brief Allocate CIO2 memory buffers and export them in a BufferPool + * \brief Allocate frame buffers for the CIO2 output * - * Allocate memory buffers in the CIO2 video device and export them to - * a buffer pool that can be imported by another device. + * Allocate frame buffers in the CIO2 video device to be used to capture frames + * from the CIO2 output. The buffers are stored in the CIO2Device::buffers_ + * vector. * - * \return The buffer pool with export buffers on success or nullptr otherwise + * \return Number of buffers allocated or negative error code */ -BufferPool *CIO2Device::exportBuffers() +int CIO2Device::allocateBuffers() { - pool_.createBuffers(CIO2_BUFFER_COUNT); - - int ret = output_->exportBuffers(&pool_); - if (ret) { + int ret = output_->exportBuffers(CIO2_BUFFER_COUNT, &buffers_); + if (ret < 0) LOG(IPU3, Error) << "Failed to export CIO2 buffers"; - return nullptr; - } - return &pool_; + return ret; } void CIO2Device::freeBuffers() { + buffers_.clear(); + if (output_->releaseBuffers()) LOG(IPU3, Error) << "Failed to release CIO2 buffers"; } -int CIO2Device::start(std::vector> *buffers) +int CIO2Device::start() { - *buffers = output_->queueAllBuffers(); - if (buffers->empty()) - return -EINVAL; + for (const std::unique_ptr &buffer : buffers_) { + int ret = output_->queueBuffer(buffer.get()); + if (ret) { + LOG(IPU3, Error) << "Failed to queue CIO2 buffer"; + return ret; + } + } return output_->streamOn(); } From patchwork Sun Jan 12 01:02:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2606 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DAA16606E7 for ; Sun, 12 Jan 2020 02:03:20 +0100 (CET) X-Halon-ID: 50be0dd9-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 50be0dd9-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:16 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:04 +0100 Message-Id: <20200112010212.2609025-25-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 24/32] libcamera: pipeline: Add FrameBuffer handlers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:21 -0000 Extend the pipeline handlers to support the FrameBuffer API with three new methods to handle allocation, importing and freeing of buffers. The new methods will replace allocateBuffers() and freeBuffers(). The FrameBuffer API will use the methods on a stream level and either allocate or import buffers for each active stream controlled from the Camera class and an upcoming FrameBufferAllocator helper. With this new API the implementation in pipeline handlers can be made simpler as all streams don't need to be handled in allocateBuffers(). Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v3 - Add documentation paragraph for PipelineHandler::exportFrameBuffers() which was lost from review of v2. * Changes since v2 - In the subject line, s/pipelines/pipeline/ - Rename allocateFrameBuffers() to exportFrameBuffers() - Rewrite of documentation, thanks Laurent! - Remove count argument from exportFrameBuffers() and importFrameBuffers() and instead get the buffer count from the stream configuration. --- src/libcamera/include/pipeline_handler.h | 5 ++ src/libcamera/pipeline/ipu3/ipu3.cpp | 32 +++++++++++ src/libcamera/pipeline/rkisp1/rkisp1.cpp | 23 ++++++++ src/libcamera/pipeline/uvcvideo.cpp | 29 ++++++++++ src/libcamera/pipeline/vimc.cpp | 29 ++++++++++ src/libcamera/pipeline_handler.cpp | 67 ++++++++++++++++++++++++ 6 files changed, 185 insertions(+) diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index 067baef56278b577..560be3bad8d59b20 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -70,6 +70,11 @@ public: const StreamRoles &roles) = 0; virtual int configure(Camera *camera, CameraConfiguration *config) = 0; + virtual int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) = 0; + virtual int importFrameBuffers(Camera *camera, Stream *stream) = 0; + virtual void freeFrameBuffers(Camera *camera, Stream *stream) = 0; + virtual int allocateBuffers(Camera *camera, const std::set &streams) = 0; virtual int freeBuffers(Camera *camera, diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 7c4539d488bd2d26..b73f0f0725ee2613 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -210,6 +210,11 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + int importFrameBuffers(Camera *camera, Stream *stream) override; + void freeFrameBuffers(Camera *camera, Stream *stream) override; + int allocateBuffers(Camera *camera, const std::set &streams) override; int freeBuffers(Camera *camera, @@ -616,6 +621,33 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c) return 0; } +int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + IPU3Stream *ipu3stream = static_cast(stream); + V4L2VideoDevice *video = ipu3stream->device_->dev; + unsigned int count = stream->configuration().bufferCount; + + return video->exportBuffers(count, buffers); +} + +int PipelineHandlerIPU3::importFrameBuffers(Camera *camera, Stream *stream) +{ + IPU3Stream *ipu3stream = static_cast(stream); + V4L2VideoDevice *video = ipu3stream->device_->dev; + unsigned int count = stream->configuration().bufferCount; + + return video->importBuffers(count); +} + +void PipelineHandlerIPU3::freeFrameBuffers(Camera *camera, Stream *stream) +{ + IPU3Stream *ipu3stream = static_cast(stream); + V4L2VideoDevice *video = ipu3stream->device_->dev; + + video->releaseBuffers(); +} + /** * \todo Clarify if 'viewfinder' and 'stat' nodes have to be set up and * started even if not in use. As of now, if not properly configured and diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index dbc5df801f30e76b..ba8f93e8584c97a9 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -174,6 +174,11 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + int importFrameBuffers(Camera *camera, Stream *stream) override; + void freeFrameBuffers(Camera *camera, Stream *stream) override; + int allocateBuffers(Camera *camera, const std::set &streams) override; int freeBuffers(Camera *camera, @@ -665,6 +670,24 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c) return 0; } +int PipelineHandlerRkISP1::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + unsigned int count = stream->configuration().bufferCount; + return video_->exportBuffers(count, buffers); +} + +int PipelineHandlerRkISP1::importFrameBuffers(Camera *camera, Stream *stream) +{ + unsigned int count = stream->configuration().bufferCount; + return video_->importBuffers(count); +} + +void PipelineHandlerRkISP1::freeFrameBuffers(Camera *camera, Stream *stream) +{ + video_->releaseBuffers(); +} + int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, const std::set &streams) { diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index ce38445d2a48ca82..b72841edee572f99 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -65,6 +65,11 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + int importFrameBuffers(Camera *camera, Stream *stream) override; + void freeFrameBuffers(Camera *camera, Stream *stream) override; + int allocateBuffers(Camera *camera, const std::set &streams) override; int freeBuffers(Camera *camera, @@ -193,6 +198,30 @@ int PipelineHandlerUVC::configure(Camera *camera, CameraConfiguration *config) return 0; } +int PipelineHandlerUVC::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + UVCCameraData *data = cameraData(camera); + unsigned int count = stream->configuration().bufferCount; + + return data->video_->exportBuffers(count, buffers); +} + +int PipelineHandlerUVC::importFrameBuffers(Camera *camera, Stream *stream) +{ + UVCCameraData *data = cameraData(camera); + unsigned int count = stream->configuration().bufferCount; + + return data->video_->importBuffers(count); +} + +void PipelineHandlerUVC::freeFrameBuffers(Camera *camera, Stream *stream) +{ + UVCCameraData *data = cameraData(camera); + + data->video_->releaseBuffers(); +} + int PipelineHandlerUVC::allocateBuffers(Camera *camera, const std::set &streams) { diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 292900bcf8d1b359..3fb5cacde0dab5b6 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -82,6 +82,11 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + int importFrameBuffers(Camera *camera, Stream *stream) override; + void freeFrameBuffers(Camera *camera, Stream *stream) override; + int allocateBuffers(Camera *camera, const std::set &streams) override; int freeBuffers(Camera *camera, @@ -259,6 +264,30 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config) return 0; } +int PipelineHandlerVimc::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + VimcCameraData *data = cameraData(camera); + unsigned int count = stream->configuration().bufferCount; + + return data->video_->exportBuffers(count, buffers); +} + +int PipelineHandlerVimc::importFrameBuffers(Camera *camera, Stream *stream) +{ + VimcCameraData *data = cameraData(camera); + unsigned int count = stream->configuration().bufferCount; + + return data->video_->importBuffers(count); +} + +void PipelineHandlerVimc::freeFrameBuffers(Camera *camera, Stream *stream) +{ + VimcCameraData *data = cameraData(camera); + + data->video_->releaseBuffers(); +} + int PipelineHandlerVimc::allocateBuffers(Camera *camera, const std::set &streams) { diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 698dd52560797d7c..572f751b5217eb5f 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -289,6 +289,73 @@ const ControlInfoMap &PipelineHandler::controls(Camera *camera) * \return 0 on success or a negative error code otherwise */ +/** + * \fn PipelineHandler::exportFrameBuffers() + * \brief Allocate buffers for \a stream + * \param[in] camera The camera + * \param[in] stream The stream to allocate buffers for + * \param[out] buffers Array of buffers successfully allocated + * + * This method allocates buffers for the \a stream from the devices associated + * with the stream in the corresponding pipeline handler. Those buffers shall be + * suitable to be added to a Request for the stream, and shall be mappable to + * the CPU through their associated dmabufs with mmap(). + * + * exportFrameBuffers() shall also allocate all other resources required by + * the pipeline handler for the stream to prepare for starting the Camera. This + * responsibility is shared with importFrameBuffers(), and one and only one of + * those two methods shall be called for each stream until the buffers are + * freed. The pipeline handler shall support all combinations of + * exportFrameBuffers() and importFrameBuffers() for the streams contained in + * any camera configuration. + * + * The only intended caller is the FrameBufferAllocator helper. + * + * \return 0 on success or a negative error code otherwise + */ + +/** + * \fn PipelineHandler::importFrameBuffers() + * \brief Prepare \a stream to use external buffers + * \param[in] camera The camera + * \param[in] stream The stream to prepare for import + * + * This method prepares the pipeline handler to use buffers provided by the + * application for the \a stream. + * + * The method may only be called after the Camera has been configured and before + * it gets started, or after it gets stopped. It shall be called only for + * streams that are part of the active camera configuration, and at most once + * per stream until buffers for the stream are freed with freeFrameBuffers(). + * + * importFrameBuffers() shall also allocate all other resources required by the + * pipeline handler for the stream to prepare for starting the Camera. This + * responsibility is shared with exportFrameBuffers(), and one and only one of + * those two methods shall be called for each stream until the buffers are + * freed. The pipeline handler shall support all combinations of + * exportFrameBuffers() and importFrameBuffers() for the streams contained in + * any camera configuration. + * + * The only intended caller is Camera::start(). + * + * \return 0 on success or a negative error code otherwise + */ + +/** + * \fn PipelineHandler::freeFrameBuffers() + * \brief Free buffers allocated from the stream + * \param[in] camera The camera + * \param[in] stream The stream to free buffers for + * + * This method shall free all buffers and all other resources allocated for the + * \a stream by exportFrameBuffers() or importFrameBuffers(). It shall be + * called only after a successful call to either of these two methods, and only + * once per stream. + * + * The only intended callers are Camera::stop() and the FrameBufferAllocator + * helper. + */ + /** * \fn PipelineHandler::allocateBuffers() * \brief Allocate buffers for a stream From patchwork Sun Jan 12 01:02:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2607 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0A3CA606DB for ; Sun, 12 Jan 2020 02:03:22 +0100 (CET) X-Halon-ID: 5162b0d8-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 5162b0d8-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:17 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:05 +0100 Message-Id: <20200112010212.2609025-26-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 25/32] libcamera: allocator: Add FrameBufferAllocator to help applications allocate buffers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:22 -0000 The FrameBuffer interface is based on the idea that all buffers are allocated externally to libcamera and are only used by it. This is meant to create a simpler API centered around usage of buffers, regardless of where they come from. Linux however lacks a centralized allocator at the moment, and not all users of libcamera are expected to use another device that could provide suitable buffers for the camera. This patch thus adds a helper class to allocate buffers internally in libcamera, in a way that matches the needs of the FrameBuffer-based API. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v3 - Code style update - Dropped leftover \brief * Changes since v2 - Replace FrameBufferAllocator::streams() with FrameBufferAllocator::allocated() - Add todo to see if we can find a better API for Camera::relase() with a active FrameBufferAllocator - Touch up documentation - Make sure stream to be allocated is part of the cameras active configuration not just the camera * Changes since v1 - Update commit message - Rename class to FrameBufferAllocator - Rename source files framebuffer_allocator.{cpp,h} - Update include headers - Update documentation - Check for double allocation for the same stream in allocate() - Add interactions with Camera::allocator_ and enforce buffers may only be allocated when the camera is in the configured state. --- include/libcamera/camera.h | 5 + include/libcamera/framebuffer_allocator.h | 45 +++++ include/libcamera/meson.build | 1 + src/libcamera/camera.cpp | 19 +- src/libcamera/framebuffer_allocator.cpp | 216 ++++++++++++++++++++++ src/libcamera/meson.build | 1 + 6 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 include/libcamera/framebuffer_allocator.h create mode 100644 src/libcamera/framebuffer_allocator.cpp diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index ef6a37bb142c83a6..2fd5870b15c69087 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -20,6 +20,7 @@ namespace libcamera { class Buffer; +class FrameBufferAllocator; class PipelineHandler; class Request; @@ -126,6 +127,10 @@ private: bool disconnected_; State state_; + + /* Needed to update allocator_ and to read state_ and activeStreams_. */ + friend class FrameBufferAllocator; + FrameBufferAllocator *allocator_; }; } /* namespace libcamera */ diff --git a/include/libcamera/framebuffer_allocator.h b/include/libcamera/framebuffer_allocator.h new file mode 100644 index 0000000000000000..42812253ada94d94 --- /dev/null +++ b/include/libcamera/framebuffer_allocator.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * framebuffer_allocator.h - FrameBuffer allocator + */ +#ifndef __LIBCAMERA_FRAMEBUFFER_ALLOCATOR_H__ +#define __LIBCAMERA_FRAMEBUFFER_ALLOCATOR_H__ + +#include +#include +#include + +namespace libcamera { + +class Camera; +class FrameBuffer; +class Stream; + +class FrameBufferAllocator +{ +public: + static FrameBufferAllocator *create(std::shared_ptr camera); + + FrameBufferAllocator(const Camera &) = delete; + FrameBufferAllocator &operator=(const Camera &) = delete; + + ~FrameBufferAllocator(); + + int allocate(Stream *stream); + int free(Stream *stream); + + bool allocated() const { return !buffers_.empty(); } + const std::vector> &buffers(Stream *stream) const; + +private: + FrameBufferAllocator(std::shared_ptr camera); + + std::shared_ptr camera_; + std::map>> buffers_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_FRAMEBUFFER_ALLOCATOR_H__ */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 543e6773cc5158a0..8db217bb782c1443 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -7,6 +7,7 @@ libcamera_api = files([ 'event_dispatcher.h', 'event_notifier.h', 'file_descriptor.h', + 'framebuffer_allocator.h', 'geometry.h', 'logging.h', 'object.h', diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 5d294b10647db8da..20340167a140f33a 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -9,6 +9,7 @@ #include +#include #include #include @@ -405,7 +406,7 @@ const std::string &Camera::name() const Camera::Camera(PipelineHandler *pipe, const std::string &name) : pipe_(pipe->shared_from_this()), name_(name), disconnected_(false), - state_(CameraAvailable) + state_(CameraAvailable), allocator_(nullptr) { } @@ -541,6 +542,16 @@ int Camera::release() if (!stateBetween(CameraAvailable, CameraConfigured)) return -EBUSY; + if (allocator_) { + /* + * \todo Try to find a better API that would make this error + * impossible. + */ + LOG(Camera, Error) + << "Buffers must be freed before the camera can be reconfigured"; + return -EBUSY; + } + pipe_->unlock(); state_ = CameraAvailable; @@ -649,6 +660,12 @@ int Camera::configure(CameraConfiguration *config) if (!stateBetween(CameraAcquired, CameraConfigured)) return -EACCES; + if (allocator_ && allocator_->allocated()) { + LOG(Camera, Error) + << "Allocator must be deleted before camera can be reconfigured"; + return -EBUSY; + } + if (config->validate() != CameraConfiguration::Valid) { LOG(Camera, Error) << "Can't configure camera with invalid configuration"; diff --git a/src/libcamera/framebuffer_allocator.cpp b/src/libcamera/framebuffer_allocator.cpp new file mode 100644 index 0000000000000000..57789b24e9615fa6 --- /dev/null +++ b/src/libcamera/framebuffer_allocator.cpp @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * framebuffer_allocator.cpp - FrameBuffer allocator + */ + +#include + +#include + +#include +#include +#include + +#include "log.h" +#include "pipeline_handler.h" + +/** + * \file framebuffer_allocator.h + * \brief FrameBuffer allocator + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Allocator) + +/** + * \class FrameBufferAllocator + * \brief FrameBuffer allocator for applications + * + * The libcamera API is designed to consume buffers provided by applications as + * FrameBuffer instances. This makes libcamera a user of buffers exported by + * other devices (such as displays or video encoders), or allocated from an + * external allocator (such as ION on Android platforms). In some situations, + * applications do not have any mean to allocate or get hold of suitable + * buffers, for instance when no other device is involved, on Linux platforms + * that lack a centralized allocator. The FrameBufferAllocator class provides a + * buffer allocator that can be used in these situations. + * + * Applications create a framebuffer allocator for a Camera, and use it to + * allocate buffers for streams of a CameraConfiguration with allocate(). They + * control which streams to allocate buffers for, and can thus use external + * buffers for a subset of the streams if desired. + * + * Buffers are deleted for a stream with free(), and destroying the allocator + * automatically deletes all allocated buffers. Applications own the buffers + * allocated by the FrameBufferAllocator and are responsible for ensuring the + * buffers are not deleted while they are in use (part of a Request that has + * been queued and hasn't completed yet). + * + * Usage of the FrameBufferAllocator is optional, if all buffers for a camera + * are provided externally applications shall not use this class. + */ + +/** + * \brief Create a FrameBuffer allocator + * \param[in] camera The camera the allocator serves + * + * A single allocator may be created for a Camera instance. + * + * The caller is responsible for deleting the allocator before the camera is + * released. + * + * \return A pointer to the newly created allocator object or nullptr on error + */ +FrameBufferAllocator * +FrameBufferAllocator::create(std::shared_ptr camera) +{ + if (camera->allocator_) { + LOG(Allocator, Error) << "Camera already has an allocator"; + return nullptr; + } + + camera->allocator_ = new FrameBufferAllocator(camera); + return camera->allocator_; +} + +/** + * \brief Construct a FrameBufferAllocator serving a camera + * \param[in] camera The camera + */ +FrameBufferAllocator::FrameBufferAllocator(std::shared_ptr camera) + : camera_(camera) +{ +} + +FrameBufferAllocator::~FrameBufferAllocator() +{ + for (auto &value : buffers_) { + Stream *stream = value.first; + camera_->pipe_->freeFrameBuffers(camera_.get(), stream); + } + + buffers_.clear(); + + camera_->allocator_ = nullptr; +} + +/** + * \brief Allocate buffers for a configured stream + * \param[in] stream The stream to allocate buffers for + * + * Allocate buffers suitable for capturing frames from the \a stream. The Camera + * shall have been previously configured with Camera::configure() and shall be + * stopped, and the stream shall be part of the active camera configuration. + * + * Upon successful allocation, the allocated buffers can be retrieved with the + * buffers() method. + * + * \return 0 on success or a negative error code otherwise + * \retval -EACCES The camera is not in a state where buffers can be allocated + * \retval -EINVAL The \a stream does not belong to the camera or the stream is + * not part of the active camera configuration + * \retval -EBUSY Buffers are already allocated for the \a stream + */ +int FrameBufferAllocator::allocate(Stream *stream) +{ + if (camera_->state_ != Camera::CameraConfigured && + camera_->state_ != Camera::CameraPrepared) { + LOG(Allocator, Error) + << "Camera must be in the configured state to allocate buffers"; + return -EACCES; + } + + if (camera_->streams().find(stream) == camera_->streams().end()) { + LOG(Allocator, Error) + << "Stream does not belong to " << camera_->name(); + return -EINVAL; + } + + if (camera_->activeStreams_.find(stream) == camera_->activeStreams_.end()) { + LOG(Allocator, Error) + << "Stream is not part of " << camera_->name() + << " active configuration"; + return -EINVAL; + } + + if (buffers_.count(stream)) { + LOG(Allocator, Error) << "Buffers already allocated for stream"; + return -EBUSY; + } + + int ret = camera_->pipe_->exportFrameBuffers(camera_.get(), stream, + &buffers_[stream]); + if (ret) + return ret; + + return 0; +} + +/** + * \brief Free buffers previously allocated for a \a stream + * \param[in] stream The stream + * + * Free buffers allocated with allocate(). + * + * This invalidates the buffers returned by buffers(). + * + * \return 0 on success or a negative error code otherwise + * \retval -EACCES The camera is not in a state where buffers can be freed + * \retval -EINVAL The allocator do not handle the \a stream + */ +int FrameBufferAllocator::free(Stream *stream) +{ + if (camera_->state_ != Camera::CameraConfigured && camera_->state_ != Camera::CameraPrepared) { + LOG(Allocator, Error) + << "Camera must be in the configured state to free buffers"; + return -EACCES; + } + + auto iter = buffers_.find(stream); + if (iter == buffers_.end()) + return -EINVAL; + + std::vector> &buffers = iter->second; + + buffers.clear(); + + camera_->pipe_->freeFrameBuffers(camera_.get(), stream); + + buffers_.erase(iter); + + return 0; +} + +/** + * \fn FrameBufferAllocator::allocated() + * \brief Check if the allocator has allocated buffers for any stream + * \return True if the allocator has allocated buffers for one or more + * streams, false otherwise + */ + +/** + * \brief Retrieve the buffers allocated for a \a stream + * \param[in] stream The stream to retrive buffers for + * + * This method shall only be called after successfully allocating buffers for + * \a stream with allocate(). The returned buffers are valid until free() is + * called for the same stream or the FrameBufferAllocator instance is destroyed. + * + * \return The buffers allocated for the \a stream + */ +const std::vector> & +FrameBufferAllocator::buffers(Stream *stream) const +{ + static std::vector> empty; + + auto iter = buffers_.find(stream); + if (iter == buffers_.end()) + return empty; + + return iter->second; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 722c5bc15afe52ef..68d89559b290befd 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -16,6 +16,7 @@ libcamera_sources = files([ 'event_notifier.cpp', 'file_descriptor.cpp', 'formats.cpp', + 'framebuffer_allocator.cpp', 'geometry.cpp', 'ipa_context_wrapper.cpp', 'ipa_controls.cpp', From patchwork Sun Jan 12 01:02:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2608 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D3909606E2 for ; Sun, 12 Jan 2020 02:03:24 +0100 (CET) X-Halon-ID: 5216d22a-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 5216d22a-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:19 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:06 +0100 Message-Id: <20200112010212.2609025-27-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 26/32] libcamera: Switch to FrameBuffer interface X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:25 -0000 Switch to the FrameBuffer interface where all buffers are treated as external buffers and are allocated outside the camera. Applications allocating buffers using libcamera are switched to use the FrameBufferAllocator helper. Follow-up changes to this one will finalize the transition to the new FrameBuffer interface by removing code that is left unused after this change. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v3 - Pass buffer index in v4l2 component using the request cookie instead of the buffer cookie. Thanks Laurent for a great suggestion. * Changes since v2 - Fix compile tested only code path in the HAL - Update documentation - Add a setCookie() call in the V4L2 compat layer which was lost in Laurent's protoype patch which lead to the index handling in V4L2 to break. --- include/libcamera/camera.h | 4 +- include/libcamera/request.h | 14 +-- src/android/camera_device.cpp | 31 ++++-- src/cam/buffer_writer.cpp | 5 +- src/cam/buffer_writer.h | 3 +- src/cam/capture.cpp | 42 ++++---- src/cam/capture.h | 5 +- src/libcamera/camera.cpp | 48 +++------ src/libcamera/include/pipeline_handler.h | 5 +- src/libcamera/pipeline/ipu3/ipu3.cpp | 124 +++++++---------------- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 41 ++------ src/libcamera/pipeline/uvcvideo.cpp | 22 ++-- src/libcamera/pipeline/vimc.cpp | 22 ++-- src/libcamera/pipeline_handler.cpp | 2 +- src/libcamera/request.cpp | 30 +++--- src/qcam/main_window.cpp | 44 ++++---- src/qcam/main_window.h | 6 +- src/v4l2/v4l2_camera.cpp | 60 ++++++----- src/v4l2/v4l2_camera.h | 39 +++---- src/v4l2/v4l2_camera_proxy.cpp | 17 ++-- test/camera/buffer_import.cpp | 19 ++-- test/camera/capture.cpp | 38 ++++--- test/camera/statemachine.cpp | 12 ++- 23 files changed, 266 insertions(+), 367 deletions(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 2fd5870b15c69087..28655bec9ebc89ce 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -19,7 +19,7 @@ namespace libcamera { -class Buffer; +class FrameBuffer; class FrameBufferAllocator; class PipelineHandler; class Request; @@ -78,7 +78,7 @@ public: const std::string &name() const; - Signal bufferCompleted; + Signal bufferCompleted; Signal requestCompleted; Signal disconnected; diff --git a/include/libcamera/request.h b/include/libcamera/request.h index b832422482645f25..eded68318b7dbb3c 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -17,9 +17,9 @@ namespace libcamera { -class Buffer; class Camera; class CameraControlValidator; +class FrameBuffer; class Stream; class Request @@ -38,9 +38,9 @@ public: ControlList &controls() { return *controls_; } ControlList &metadata() { return *metadata_; } - const std::map &buffers() const { return bufferMap_; } - int addBuffer(Stream *stream, std::unique_ptr buffer); - Buffer *findBuffer(Stream *stream) const; + const std::map &buffers() const { return bufferMap_; } + int addBuffer(Stream *stream, FrameBuffer *buffer); + FrameBuffer *findBuffer(Stream *stream) const; uint64_t cookie() const { return cookie_; } Status status() const { return status_; } @@ -52,14 +52,14 @@ private: void complete(); - bool completeBuffer(Buffer *buffer); + bool completeBuffer(FrameBuffer *buffer); Camera *camera_; CameraControlValidator *validator_; ControlList *controls_; ControlList *metadata_; - std::map bufferMap_; - std::unordered_set pending_; + std::map bufferMap_; + std::unordered_set pending_; const uint64_t cookie_; Status status_; diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index ebe91ea8af4f6436..ab3e44889f8b7ada 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -739,13 +739,21 @@ void CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reque * and (currently) only supported request buffer. */ const buffer_handle_t camera3Handle = *camera3Buffers[0].buffer; - std::array fds = { - camera3Handle->data[0], - camera3Handle->data[1], - camera3Handle->data[2], - }; - std::unique_ptr buffer = stream->createBuffer(fds); + std::vector planes; + for (int i = 0; i < 3; i++) { + FrameBuffer::Plane plane; + plane.fd = FileDescriptor(camera3Handle->data[i]); + /* + * Setting length to zero here is OK as the length is only used + * to map the memory of the plane. Libcamera do not need to poke + * at the memory content queued by the HAL. + */ + plane.length = 0; + planes.push_back(std::move(plane)); + } + + FrameBuffer *buffer = new FrameBuffer(std::move(planes)); if (!buffer) { LOG(HAL, Error) << "Failed to create buffer"; delete descriptor; @@ -754,7 +762,7 @@ void CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reque Request *request = camera_->createRequest(reinterpret_cast(descriptor)); - request->addBuffer(stream, std::move(buffer)); + request->addBuffer(stream, buffer); int ret = camera_->queueRequest(request); if (ret) { @@ -771,8 +779,8 @@ error: void CameraDevice::requestComplete(Request *request) { - const std::map &buffers = request->buffers(); - Buffer *libcameraBuffer = buffers.begin()->second; + const std::map &buffers = request->buffers(); + FrameBuffer *buffer = buffers.begin()->second; camera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK; std::unique_ptr resultMetadata; @@ -803,11 +811,11 @@ void CameraDevice::requestComplete(Request *request) if (status == CAMERA3_BUFFER_STATUS_OK) { notifyShutter(descriptor->frameNumber, - libcameraBuffer->metadata().timestamp); + buffer->metadata().timestamp); captureResult.partial_result = 1; resultMetadata = getResultMetadata(descriptor->frameNumber, - libcameraBuffer->metadata().timestamp); + buffer->metadata().timestamp); captureResult.result = resultMetadata->get(); } @@ -825,6 +833,7 @@ void CameraDevice::requestComplete(Request *request) callbacks_->process_capture_result(callbacks_, &captureResult); delete descriptor; + delete buffer; } void CameraDevice::notifyShutter(uint32_t frameNumber, uint64_t timestamp) diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp index 41ef4b0a36d61b03..1d7366c87714cd91 100644 --- a/src/cam/buffer_writer.cpp +++ b/src/cam/buffer_writer.cpp @@ -22,7 +22,7 @@ BufferWriter::BufferWriter(const std::string &pattern) { } -int BufferWriter::write(Buffer *buffer, const std::string &streamName) +int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName) { std::string filename; size_t pos; @@ -43,8 +43,7 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName) if (fd == -1) return -errno; - BufferMemory *mem = buffer->mem(); - for (const FrameBuffer::Plane &plane : mem->planes()) { + for (const FrameBuffer::Plane &plane : buffer->planes()) { /* \todo Once the FrameBuffer is done cache mapped memory. */ void *data = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, plane.fd.fd(), 0); diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h index 7bf785d1e83235ff..5917a7dfb5e28106 100644 --- a/src/cam/buffer_writer.h +++ b/src/cam/buffer_writer.h @@ -16,7 +16,8 @@ class BufferWriter public: BufferWriter(const std::string &pattern = "frame-#.bin"); - int write(libcamera::Buffer *buffer, const std::string &streamName); + int write(libcamera::FrameBuffer *buffer, + const std::string &streamName); private: std::string pattern_; diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp index da942f56118983bd..dd078eb0ae4a2c62 100644 --- a/src/cam/capture.cpp +++ b/src/cam/capture.cpp @@ -57,7 +57,10 @@ int Capture::run(EventLoop *loop, const OptionsParser::Options &options) writer_ = new BufferWriter(); } - ret = capture(loop); + + FrameBufferAllocator *allocator = FrameBufferAllocator::create(camera_); + + ret = capture(loop, allocator); if (options.isSet(OptFile)) { delete writer_; @@ -66,17 +69,27 @@ int Capture::run(EventLoop *loop, const OptionsParser::Options &options) camera_->freeBuffers(); + delete allocator; + return ret; } -int Capture::capture(EventLoop *loop) +int Capture::capture(EventLoop *loop, FrameBufferAllocator *allocator) { int ret; /* Identify the stream with the least number of buffers. */ unsigned int nbuffers = UINT_MAX; - for (StreamConfiguration &cfg : *config_) - nbuffers = std::min(nbuffers, cfg.bufferCount); + for (StreamConfiguration &cfg : *config_) { + ret = allocator->allocate(cfg.stream()); + if (ret < 0) { + std::cerr << "Can't allocate buffers" << std::endl; + return -ENOMEM; + } + + unsigned int allocated = allocator->buffers(cfg.stream()).size(); + nbuffers = std::min(nbuffers, allocated); + } /* * TODO: make cam tool smarter to support still capture by for @@ -93,9 +106,11 @@ int Capture::capture(EventLoop *loop) for (StreamConfiguration &cfg : *config_) { Stream *stream = cfg.stream(); - std::unique_ptr buffer = stream->createBuffer(i); + const std::vector> &buffers = + allocator->buffers(stream); + const std::unique_ptr &buffer = buffers[i]; - ret = request->addBuffer(stream, std::move(buffer)); + ret = request->addBuffer(stream, buffer.get()); if (ret < 0) { std::cerr << "Can't set buffer for request" << std::endl; @@ -138,7 +153,7 @@ void Capture::requestComplete(Request *request) if (request->status() == Request::RequestCancelled) return; - const std::map &buffers = request->buffers(); + const std::map &buffers = request->buffers(); std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); double fps = std::chrono::duration_cast(now - last_).count(); @@ -151,7 +166,7 @@ void Capture::requestComplete(Request *request) for (auto it = buffers.begin(); it != buffers.end(); ++it) { Stream *stream = it->first; - Buffer *buffer = it->second; + FrameBuffer *buffer = it->second; const std::string &name = streamName_[stream]; const FrameMetadata &metadata = buffer->metadata(); @@ -185,16 +200,9 @@ void Capture::requestComplete(Request *request) for (auto it = buffers.begin(); it != buffers.end(); ++it) { Stream *stream = it->first; - Buffer *buffer = it->second; - unsigned int index = buffer->index(); - - std::unique_ptr newBuffer = stream->createBuffer(index); - if (!newBuffer) { - std::cerr << "Can't create buffer" << std::endl; - return; - } + FrameBuffer *buffer = it->second; - request->addBuffer(stream, std::move(newBuffer)); + request->addBuffer(stream, buffer); } camera_->queueRequest(request); diff --git a/src/cam/capture.h b/src/cam/capture.h index c692d48918f2de1d..9bca5661070efcf5 100644 --- a/src/cam/capture.h +++ b/src/cam/capture.h @@ -10,7 +10,9 @@ #include #include +#include #include +#include #include #include @@ -26,7 +28,8 @@ public: int run(EventLoop *loop, const OptionsParser::Options &options); private: - int capture(EventLoop *loop); + int capture(EventLoop *loop, + libcamera::FrameBufferAllocator *allocator); void requestComplete(libcamera::Request *request); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 20340167a140f33a..3fe40feb88be7324 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -695,12 +695,6 @@ int Camera::configure(CameraConfiguration *config) stream->configuration_ = cfg; activeStreams_.insert(stream); - - /* - * Allocate buffer objects in the pool. - * Memory will be allocated and assigned later. - */ - stream->createBuffers(cfg.memoryType, cfg.bufferCount); } state_ = CameraConfigured; @@ -756,14 +750,6 @@ int Camera::freeBuffers() if (!stateIs(CameraPrepared)) return -EACCES; - for (Stream *stream : activeStreams_) { - /* - * All mappings must be destroyed before buffers can be freed - * by the V4L2 device that has allocated them. - */ - stream->destroyBuffers(); - } - state_ = CameraConfigured; return pipe_->freeBuffers(this, activeStreams_); @@ -834,24 +820,11 @@ int Camera::queueRequest(Request *request) for (auto const &it : request->buffers()) { Stream *stream = it.first; - Buffer *buffer = it.second; if (activeStreams_.find(stream) == activeStreams_.end()) { LOG(Camera, Error) << "Invalid request"; return -EINVAL; } - - if (stream->memoryType() == ExternalMemory) { - int index = stream->mapBuffer(buffer); - if (index < 0) { - LOG(Camera, Error) << "No buffer memory available"; - return -ENOMEM; - } - - buffer->index_ = index; - } - - buffer->mem_ = &stream->buffers()[buffer->index_]; } return pipe_->queueRequest(this, request); @@ -880,6 +853,13 @@ int Camera::start() LOG(Camera, Debug) << "Starting capture"; + for (Stream *stream : activeStreams_) { + if (allocator_ && !allocator_->buffers(stream).empty()) + continue; + + pipe_->importFrameBuffers(this, stream); + } + int ret = pipe_->start(this); if (ret) return ret; @@ -915,6 +895,13 @@ int Camera::stop() pipe_->stop(this); + for (Stream *stream : activeStreams_) { + if (allocator_ && !allocator_->buffers(stream).empty()) + continue; + + pipe_->freeFrameBuffers(this, stream); + } + return 0; } @@ -928,13 +915,6 @@ int Camera::stop() */ void Camera::requestComplete(Request *request) { - for (auto it : request->buffers()) { - Stream *stream = it.first; - Buffer *buffer = it.second; - if (stream->memoryType() == ExternalMemory) - stream->unmapBuffer(buffer); - } - requestCompleted.emit(request); delete request; } diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index 560be3bad8d59b20..27f3852c6c87843f 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -21,12 +21,12 @@ namespace libcamera { -class Buffer; class Camera; class CameraConfiguration; class CameraManager; class DeviceEnumerator; class DeviceMatch; +class FrameBuffer; class MediaDevice; class PipelineHandler; class Request; @@ -85,7 +85,8 @@ public: int queueRequest(Camera *camera, Request *request); - bool completeBuffer(Camera *camera, Request *request, Buffer *buffer); + bool completeBuffer(Camera *camera, Request *request, + FrameBuffer *buffer); void completeRequest(Camera *camera, Request *request); const char *name() const { return name_; } diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index b73f0f0725ee2613..065f5d980b68e1cf 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -31,6 +31,8 @@ namespace libcamera { LOG_DEFINE_CATEGORY(IPU3) +class IPU3CameraData; + class ImgUDevice { public: @@ -44,7 +46,6 @@ public: V4L2VideoDevice *dev; unsigned int pad; std::string name; - BufferPool *pool; std::vector> buffers; }; @@ -71,9 +72,7 @@ public: int configureOutput(ImgUOutput *output, const StreamConfiguration &cfg); - int importOutputBuffers(ImgUOutput *output, BufferPool *pool); - int exportOutputBuffers(ImgUOutput *output, BufferPool *pool); - void freeBuffers(); + void freeBuffers(IPU3CameraData *data); int start(); int stop(); @@ -93,9 +92,6 @@ public: ImgUOutput viewfinder_; ImgUOutput stat_; /* \todo Add param video device for 3A tuning */ - - BufferPool vfPool_; - BufferPool outPool_; }; class CIO2Device @@ -156,7 +152,7 @@ public: { } - void imguOutputBufferReady(Buffer *buffer); + void imguOutputBufferReady(FrameBuffer *buffer); void imguInputBufferReady(FrameBuffer *buffer); void cio2BufferReady(FrameBuffer *buffer); @@ -691,39 +687,30 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera, goto error; } - /* Allocate buffers for each active stream. */ - for (Stream *s : streams) { - IPU3Stream *stream = static_cast(s); - ImgUDevice::ImgUOutput *dev = stream->device_; - - if (stream->memoryType() == InternalMemory) - ret = imgu->exportOutputBuffers(dev, &stream->bufferPool()); - else - ret = imgu->importOutputBuffers(dev, &stream->bufferPool()); - if (ret) - goto error; - } - /* * Allocate buffers also on non-active outputs; use the same number * of buffers as the active ones. */ if (!outStream->active_) { - bufferCount = vfStream->configuration().bufferCount; - outStream->device_->pool->createBuffers(bufferCount); - ret = imgu->exportOutputBuffers(outStream->device_, - outStream->device_->pool); - if (ret) + ImgUDevice::ImgUOutput *output = outStream->device_; + + ret = output->dev->exportBuffers(bufferCount, &output->buffers); + if (ret < 0) { + LOG(IPU3, Error) << "Failed to allocate ImgU " + << output->name << " buffers"; goto error; + } } if (!vfStream->active_) { - bufferCount = outStream->configuration().bufferCount; - vfStream->device_->pool->createBuffers(bufferCount); - ret = imgu->exportOutputBuffers(vfStream->device_, - vfStream->device_->pool); - if (ret) + ImgUDevice::ImgUOutput *output = vfStream->device_; + + ret = output->dev->exportBuffers(bufferCount, &output->buffers); + if (ret < 0) { + LOG(IPU3, Error) << "Failed to allocate ImgU " + << output->name << " buffers"; goto error; + } } return 0; @@ -740,7 +727,7 @@ int PipelineHandlerIPU3::freeBuffers(Camera *camera, IPU3CameraData *data = cameraData(camera); data->cio2_.freeBuffers(); - data->imgu_->freeBuffers(); + data->imgu_->freeBuffers(data); return 0; } @@ -793,7 +780,7 @@ int PipelineHandlerIPU3::queueRequestDevice(Camera *camera, Request *request) for (auto it : request->buffers()) { IPU3Stream *stream = static_cast(it.first); - Buffer *buffer = it.second; + FrameBuffer *buffer = it.second; int ret = stream->device_->dev->queueBuffer(buffer); if (ret < 0) @@ -920,9 +907,9 @@ int PipelineHandlerIPU3::registerCameras() &IPU3CameraData::cio2BufferReady); data->imgu_->input_->frameBufferReady.connect(data.get(), &IPU3CameraData::imguInputBufferReady); - data->imgu_->output_.dev->bufferReady.connect(data.get(), + data->imgu_->output_.dev->frameBufferReady.connect(data.get(), &IPU3CameraData::imguOutputBufferReady); - data->imgu_->viewfinder_.dev->bufferReady.connect(data.get(), + data->imgu_->viewfinder_.dev->frameBufferReady.connect(data.get(), &IPU3CameraData::imguOutputBufferReady); /* Create and register the Camera instance. */ @@ -971,7 +958,7 @@ void IPU3CameraData::imguInputBufferReady(FrameBuffer *buffer) * * Buffers completed from the ImgU output are directed to the application. */ -void IPU3CameraData::imguOutputBufferReady(Buffer *buffer) +void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer) { Request *request = buffer->request(); @@ -1046,7 +1033,6 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index) output_.pad = PAD_OUTPUT; output_.name = "output"; - output_.pool = &outPool_; viewfinder_.dev = V4L2VideoDevice::fromEntityName(media, name_ + " viewfinder"); @@ -1056,7 +1042,6 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index) viewfinder_.pad = PAD_VF; viewfinder_.name = "viewfinder"; - viewfinder_.pool = &vfPool_; stat_.dev = V4L2VideoDevice::fromEntityName(media, name_ + " 3a stat"); ret = stat_.dev->open(); @@ -1164,69 +1149,28 @@ int ImgUDevice::configureOutput(ImgUOutput *output, return 0; } -/** - * \brief Export buffers from \a output to the provided \a pool - * \param[in] output The ImgU output device - * \param[in] pool The buffer pool where to export buffers - * - * Export memory buffers reserved in the video device memory associated with - * \a output id to the buffer pool provided as argument. - * - * \return 0 on success or a negative error code otherwise - */ -int ImgUDevice::exportOutputBuffers(ImgUOutput *output, BufferPool *pool) -{ - int ret = output->dev->exportBuffers(pool); - if (ret) { - LOG(IPU3, Error) << "Failed to export ImgU " - << output->name << " buffers"; - return ret; - } - - return 0; -} - -/** - * \brief Reserve buffers in \a output from the provided \a pool - * \param[in] output The ImgU output device - * \param[in] pool The buffer pool used to reserve buffers in \a output - * - * Reserve a number of buffers equal to the number of buffers in \a pool - * in the \a output device. - * - * \return 0 on success or a negative error code otherwise - */ -int ImgUDevice::importOutputBuffers(ImgUOutput *output, BufferPool *pool) -{ - int ret = output->dev->importBuffers(pool); - if (ret) { - LOG(IPU3, Error) - << "Failed to import buffer in " << output->name - << " ImgU device"; - return ret; - } - - return 0; -} - /** * \brief Release buffers for all the ImgU video devices */ -void ImgUDevice::freeBuffers() +void ImgUDevice::freeBuffers(IPU3CameraData *data) { int ret; - ret = output_.dev->releaseBuffers(); - if (ret) - LOG(IPU3, Error) << "Failed to release ImgU output buffers"; + if (!data->outStream_.active_) { + ret = output_.dev->releaseBuffers(); + if (ret) + LOG(IPU3, Error) << "Failed to release ImgU output buffers"; + } ret = stat_.dev->releaseBuffers(); if (ret) LOG(IPU3, Error) << "Failed to release ImgU stat buffers"; - ret = viewfinder_.dev->releaseBuffers(); - if (ret) - LOG(IPU3, Error) << "Failed to release ImgU viewfinder buffers"; + if (!data->vfStream_.active_) { + ret = viewfinder_.dev->releaseBuffers(); + if (ret) + LOG(IPU3, Error) << "Failed to release ImgU viewfinder buffers"; + } ret = input_->releaseBuffers(); if (ret) diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index ba8f93e8584c97a9..d669d2ded89607d2 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -51,7 +51,7 @@ struct RkISP1FrameInfo { FrameBuffer *paramBuffer; FrameBuffer *statBuffer; - Buffer *videoBuffer; + FrameBuffer *videoBuffer; bool paramFilled; bool paramDequeued; @@ -67,7 +67,6 @@ public: int destroy(unsigned int frame); RkISP1FrameInfo *find(unsigned int frame); - RkISP1FrameInfo *find(Buffer *buffer); RkISP1FrameInfo *find(FrameBuffer *buffer); RkISP1FrameInfo *find(Request *request); @@ -87,7 +86,7 @@ public: setDelay(QueueBuffers, -1, 10); } - void bufferReady(Buffer *buffer) + void bufferReady(FrameBuffer *buffer) { /* * Calculate SOE by taking the end of DMA set by the kernel and applying @@ -205,7 +204,7 @@ private: int initLinks(); int createCamera(MediaEntity *sensor); void tryCompleteRequest(Request *request); - void bufferReady(Buffer *buffer); + void bufferReady(FrameBuffer *buffer); void paramReady(FrameBuffer *buffer); void statReady(FrameBuffer *buffer); @@ -243,7 +242,7 @@ RkISP1FrameInfo *RkISP1Frames::create(unsigned int frame, Request *request, Stre } FrameBuffer *statBuffer = pipe_->availableStatBuffers_.front(); - Buffer *videoBuffer = request->findBuffer(stream); + FrameBuffer *videoBuffer = request->findBuffer(stream); if (!videoBuffer) { LOG(RkISP1, Error) << "Attempt to queue request with invalid stream"; @@ -296,26 +295,14 @@ RkISP1FrameInfo *RkISP1Frames::find(unsigned int frame) return nullptr; } -RkISP1FrameInfo *RkISP1Frames::find(Buffer *buffer) -{ - for (auto &itInfo : frameInfo_) { - RkISP1FrameInfo *info = itInfo.second; - - if (info->videoBuffer == buffer) - return info; - } - - LOG(RkISP1, Error) << "Can't locate info from buffer"; - return nullptr; -} - RkISP1FrameInfo *RkISP1Frames::find(FrameBuffer *buffer) { for (auto &itInfo : frameInfo_) { RkISP1FrameInfo *info = itInfo.second; if (info->paramBuffer == buffer || - info->statBuffer == buffer) + info->statBuffer == buffer || + info->videoBuffer == buffer) return info; } @@ -692,18 +679,9 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, const std::set &streams) { RkISP1CameraData *data = cameraData(camera); - Stream *stream = *streams.begin(); unsigned int count = 1; int ret; - if (stream->memoryType() == InternalMemory) - ret = video_->exportBuffers(&stream->bufferPool()); - else - ret = video_->importBuffers(&stream->bufferPool()); - - if (ret) - return ret; - unsigned int maxBuffers = 0; for (const Stream *s : camera->streams()) maxBuffers = std::max(maxBuffers, s->configuration().bufferCount); @@ -769,9 +747,6 @@ int PipelineHandlerRkISP1::freeBuffers(Camera *camera, if (stat_->releaseBuffers()) LOG(RkISP1, Error) << "Failed to release stat buffers"; - if (video_->releaseBuffers()) - LOG(RkISP1, Error) << "Failed to release video buffers"; - return 0; } @@ -975,7 +950,7 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator) if (param_->open() < 0) return false; - video_->bufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady); + video_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady); stat_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::statReady); param_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::paramReady); @@ -1024,7 +999,7 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request) completeRequest(activeCamera_, request); } -void PipelineHandlerRkISP1::bufferReady(Buffer *buffer) +void PipelineHandlerRkISP1::bufferReady(FrameBuffer *buffer) { ASSERT(activeCamera_); RkISP1CameraData *data = cameraData(activeCamera_); diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index b72841edee572f99..66380e29e1a78e13 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -42,7 +42,7 @@ public: } int init(MediaEntity *entity); - void bufferReady(Buffer *buffer); + void bufferReady(FrameBuffer *buffer); V4L2VideoDevice *video_; Stream stream_; @@ -225,23 +225,13 @@ void PipelineHandlerUVC::freeFrameBuffers(Camera *camera, Stream *stream) int PipelineHandlerUVC::allocateBuffers(Camera *camera, const std::set &streams) { - UVCCameraData *data = cameraData(camera); - Stream *stream = *streams.begin(); - const StreamConfiguration &cfg = stream->configuration(); - - LOG(UVC, Debug) << "Requesting " << cfg.bufferCount << " buffers"; - - if (stream->memoryType() == InternalMemory) - return data->video_->exportBuffers(&stream->bufferPool()); - else - return data->video_->importBuffers(&stream->bufferPool()); + return 0; } int PipelineHandlerUVC::freeBuffers(Camera *camera, const std::set &streams) { - UVCCameraData *data = cameraData(camera); - return data->video_->releaseBuffers(); + return 0; } int PipelineHandlerUVC::start(Camera *camera) @@ -295,7 +285,7 @@ int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request) int PipelineHandlerUVC::queueRequestDevice(Camera *camera, Request *request) { UVCCameraData *data = cameraData(camera); - Buffer *buffer = request->findBuffer(&data->stream_); + FrameBuffer *buffer = request->findBuffer(&data->stream_); if (!buffer) { LOG(UVC, Error) << "Attempt to queue request with invalid stream"; @@ -362,7 +352,7 @@ int UVCCameraData::init(MediaEntity *entity) if (ret) return ret; - video_->bufferReady.connect(this, &UVCCameraData::bufferReady); + video_->frameBufferReady.connect(this, &UVCCameraData::bufferReady); /* Initialise the supported controls. */ const ControlInfoMap &controls = video_->controls(); @@ -402,7 +392,7 @@ int UVCCameraData::init(MediaEntity *entity) return 0; } -void UVCCameraData::bufferReady(Buffer *buffer) +void UVCCameraData::bufferReady(FrameBuffer *buffer) { Request *request = buffer->request(); diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 3fb5cacde0dab5b6..5e0f5c63b7fbe86a 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -55,7 +55,7 @@ public: } int init(MediaDevice *media); - void bufferReady(Buffer *buffer); + void bufferReady(FrameBuffer *buffer); CameraSensor *sensor_; V4L2Subdevice *debayer_; @@ -291,23 +291,13 @@ void PipelineHandlerVimc::freeFrameBuffers(Camera *camera, Stream *stream) int PipelineHandlerVimc::allocateBuffers(Camera *camera, const std::set &streams) { - VimcCameraData *data = cameraData(camera); - Stream *stream = *streams.begin(); - const StreamConfiguration &cfg = stream->configuration(); - - LOG(VIMC, Debug) << "Requesting " << cfg.bufferCount << " buffers"; - - if (stream->memoryType() == InternalMemory) - return data->video_->exportBuffers(&stream->bufferPool()); - else - return data->video_->importBuffers(&stream->bufferPool()); + return 0; } int PipelineHandlerVimc::freeBuffers(Camera *camera, const std::set &streams) { - VimcCameraData *data = cameraData(camera); - return data->video_->releaseBuffers(); + return 0; } int PipelineHandlerVimc::start(Camera *camera) @@ -355,7 +345,7 @@ int PipelineHandlerVimc::processControls(VimcCameraData *data, Request *request) int PipelineHandlerVimc::queueRequestDevice(Camera *camera, Request *request) { VimcCameraData *data = cameraData(camera); - Buffer *buffer = request->findBuffer(&data->stream_); + FrameBuffer *buffer = request->findBuffer(&data->stream_); if (!buffer) { LOG(VIMC, Error) << "Attempt to queue request with invalid stream"; @@ -447,7 +437,7 @@ int VimcCameraData::init(MediaDevice *media) if (video_->open()) return -ENODEV; - video_->bufferReady.connect(this, &VimcCameraData::bufferReady); + video_->frameBufferReady.connect(this, &VimcCameraData::bufferReady); raw_ = new V4L2VideoDevice(media->getEntityByName("Raw Capture 1")); if (raw_->open()) @@ -484,7 +474,7 @@ int VimcCameraData::init(MediaDevice *media) return 0; } -void VimcCameraData::bufferReady(Buffer *buffer) +void VimcCameraData::bufferReady(FrameBuffer *buffer) { Request *request = buffer->request(); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 572f751b5217eb5f..0348e3cfa68ed6ec 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -467,7 +467,7 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request) * otherwise */ bool PipelineHandler::completeBuffer(Camera *camera, Request *request, - Buffer *buffer) + FrameBuffer *buffer) { camera->bufferCompleted.emit(request, buffer); return request->completeBuffer(buffer); diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 4268e31434bf4feb..ea33736fbeb56505 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -75,11 +75,6 @@ Request::Request(Camera *camera, uint64_t cookie) Request::~Request() { - for (auto it : bufferMap_) { - Buffer *buffer = it.second; - delete buffer; - } - delete metadata_; delete controls_; delete validator_; @@ -106,18 +101,19 @@ Request::~Request() * \brief Retrieve the request's streams to buffers map * * Return a reference to the map that associates each Stream part of the - * request to the Buffer the Stream output should be directed to. + * request to the FrameBuffer the Stream output should be directed to. * - * \return The map of Stream to Buffer + * \return The map of Stream to FrameBuffer */ /** - * \brief Store a Buffer with its associated Stream in the Request + * \brief Add a FrameBuffer with its associated Stream to the Request * \param[in] stream The stream the buffer belongs to - * \param[in] buffer The Buffer to store in the request + * \param[in] buffer The FrameBuffer to add to the request * - * Ownership of the buffer is passed to the request. It will be deleted when - * the request is destroyed after completing. + * A reference to the buffer is stored in the request. The caller is responsible + * for ensuring that the buffer will remain valid until the request complete + * callback is called. * * A request can only contain one buffer per stream. If a buffer has already * been added to the request for the same stream, this method returns -EEXIST. @@ -126,7 +122,7 @@ Request::~Request() * \retval -EEXIST The request already contains a buffer for the stream * \retval -EINVAL The buffer does not reference a valid Stream */ -int Request::addBuffer(Stream *stream, std::unique_ptr buffer) +int Request::addBuffer(Stream *stream, FrameBuffer *buffer) { if (!stream) { LOG(Request, Error) << "Invalid stream reference"; @@ -135,13 +131,13 @@ int Request::addBuffer(Stream *stream, std::unique_ptr buffer) auto it = bufferMap_.find(stream); if (it != bufferMap_.end()) { - LOG(Request, Error) << "Buffer already set for stream"; + LOG(Request, Error) << "FrameBuffer already set for stream"; return -EEXIST; } buffer->request_ = this; - pending_.insert(buffer.get()); - bufferMap_[stream] = buffer.release(); + pending_.insert(buffer); + bufferMap_[stream] = buffer; return 0; } @@ -161,7 +157,7 @@ int Request::addBuffer(Stream *stream, std::unique_ptr buffer) * \return The buffer associated with the stream, or nullptr if the stream is * not part of this request */ -Buffer *Request::findBuffer(Stream *stream) const +FrameBuffer *Request::findBuffer(Stream *stream) const { auto it = bufferMap_.find(stream); if (it == bufferMap_.end()) @@ -231,7 +227,7 @@ void Request::complete() * \return True if all buffers contained in the request have completed, false * otherwise */ -bool Request::completeBuffer(Buffer *buffer) +bool Request::completeBuffer(FrameBuffer *buffer) { int ret = pending_.erase(buffer); ASSERT(ret == 1); diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index ca3464babdc1c313..701a2b9a73d53d96 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -23,7 +23,7 @@ using namespace libcamera; MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) - : options_(options), isCapturing_(false) + : options_(options), allocator_(nullptr), isCapturing_(false) { int ret; @@ -37,8 +37,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) adjustSize(); ret = openCamera(cm); - if (!ret) + if (!ret) { + allocator_ = FrameBufferAllocator::create(camera_); ret = startCapture(); + } if (ret < 0) QTimer::singleShot(0, QCoreApplication::instance(), @@ -49,6 +51,7 @@ MainWindow::~MainWindow() { if (camera_) { stopCapture(); + delete allocator_; camera_->release(); camera_.reset(); } @@ -176,8 +179,14 @@ int MainWindow::startCapture() return ret; } + ret = allocator_->allocate(stream); + if (ret < 0) { + std::cerr << "Failed to allocate capture buffers" << std::endl; + return ret; + } + std::vector requests; - for (unsigned int i = 0; i < cfg.bufferCount; ++i) { + for (const std::unique_ptr &buffer : allocator_->buffers(stream)) { Request *request = camera_->createRequest(); if (!request) { std::cerr << "Can't create request" << std::endl; @@ -185,13 +194,7 @@ int MainWindow::startCapture() goto error; } - std::unique_ptr buffer = stream->createBuffer(i); - if (!buffer) { - std::cerr << "Can't create buffer " << i << std::endl; - goto error; - } - - ret = request->addBuffer(stream, std::move(buffer)); + ret = request->addBuffer(stream, buffer.get()); if (ret < 0) { std::cerr << "Can't set buffer for request" << std::endl; goto error; @@ -254,11 +257,11 @@ void MainWindow::requestComplete(Request *request) if (request->status() == Request::RequestCancelled) return; - const std::map &buffers = request->buffers(); + const std::map &buffers = request->buffers(); framesCaptured_++; - Buffer *buffer = buffers.begin()->second; + FrameBuffer *buffer = buffers.begin()->second; const FrameMetadata &metadata = buffer->metadata(); double fps = metadata.timestamp - lastBufferTime_; @@ -281,28 +284,21 @@ void MainWindow::requestComplete(Request *request) for (auto it = buffers.begin(); it != buffers.end(); ++it) { Stream *stream = it->first; - Buffer *buffer = it->second; - unsigned int index = buffer->index(); - - std::unique_ptr newBuffer = stream->createBuffer(index); - if (!newBuffer) { - std::cerr << "Can't create buffer" << std::endl; - return; - } + FrameBuffer *buffer = it->second; - request->addBuffer(stream, std::move(newBuffer)); + request->addBuffer(stream, buffer); } camera_->queueRequest(request); } -int MainWindow::display(Buffer *buffer) +int MainWindow::display(FrameBuffer *buffer) { - if (buffer->mem()->planes().size() != 1) + if (buffer->planes().size() != 1) return -EINVAL; /* \todo Once the FrameBuffer is done cache mapped memory. */ - const FrameBuffer::Plane &plane = buffer->mem()->planes().front(); + const FrameBuffer::Plane &plane = buffer->planes().front(); void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, plane.fd.fd(), 0); diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 0786e915ec512255..05cde4ceab5f7ea1 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -14,8 +14,10 @@ #include #include +#include #include #include +#include #include #include "../cam/options.h" @@ -49,7 +51,7 @@ private: void stopCapture(); void requestComplete(Request *request); - int display(Buffer *buffer); + int display(FrameBuffer *buffer); QString title_; QTimer titleTimer_; @@ -57,6 +59,8 @@ private: const OptionsParser::Options &options_; std::shared_ptr camera_; + FrameBufferAllocator *allocator_; + bool isCapturing_; std::unique_ptr config_; diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index e4a03c414480f353..10db15d6276d9bf3 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -16,24 +16,15 @@ using namespace libcamera; LOG_DECLARE_CATEGORY(V4L2Compat); -V4L2FrameMetadata::V4L2FrameMetadata(Buffer *buffer) - : index_(buffer->index()), - bytesused_(buffer->metadata().planes[0].bytesused), - timestamp_(buffer->metadata().timestamp), - sequence_(buffer->metadata().sequence), - status_(buffer->metadata().status) -{ -} - V4L2Camera::V4L2Camera(std::shared_ptr camera) - : camera_(camera), isRunning_(false) + : camera_(camera), isRunning_(false), bufferAllocator_(nullptr) { camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete); } V4L2Camera::~V4L2Camera() { - camera_->release(); + close(); } int V4L2Camera::open() @@ -50,11 +41,16 @@ int V4L2Camera::open() return -EINVAL; } + bufferAllocator_ = FrameBufferAllocator::create(camera_); + return 0; } void V4L2Camera::close() { + delete bufferAllocator_; + bufferAllocator_ = nullptr; + camera_->release(); } @@ -63,12 +59,12 @@ void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig) *streamConfig = config_->at(0); } -std::vector V4L2Camera::completedBuffers() +std::vector V4L2Camera::completedBuffers() { - std::vector v; + std::vector v; bufferLock_.lock(); - for (std::unique_ptr &metadata : completedBuffers_) + for (std::unique_ptr &metadata : completedBuffers_) v.push_back(*metadata.get()); completedBuffers_.clear(); bufferLock_.unlock(); @@ -83,9 +79,9 @@ void V4L2Camera::requestComplete(Request *request) /* We only have one stream at the moment. */ bufferLock_.lock(); - Buffer *buffer = request->buffers().begin()->second; - std::unique_ptr metadata = - utils::make_unique(buffer); + FrameBuffer *buffer = request->buffers().begin()->second; + std::unique_ptr metadata = + utils::make_unique(request->cookie(), buffer->metadata()); completedBuffers_.push_back(std::move(metadata)); bufferLock_.unlock(); @@ -126,18 +122,31 @@ int V4L2Camera::configure(StreamConfiguration *streamConfigOut, int V4L2Camera::allocBuffers(unsigned int count) { int ret = camera_->allocateBuffers(); - return ret == -EACCES ? -EBUSY : ret; + if (ret) + return ret == -EACCES ? -EBUSY : ret; + + Stream *stream = *camera_->streams().begin(); + + return bufferAllocator_->allocate(stream); } void V4L2Camera::freeBuffers() { + Stream *stream = *camera_->streams().begin(); + bufferAllocator_->free(stream); camera_->freeBuffers(); } FileDescriptor V4L2Camera::getBufferFd(unsigned int index) { Stream *stream = *camera_->streams().begin(); - return stream->buffers()[index].planes()[0].fd; + const std::vector> &buffers = + bufferAllocator_->buffers(stream); + + if (buffers.size() <= index) + return FileDescriptor(); + + return buffers[index]->planes()[0].fd; } int V4L2Camera::streamOn() @@ -180,21 +189,16 @@ int V4L2Camera::streamOff() int V4L2Camera::qbuf(unsigned int index) { - Stream *stream = config_->at(0).stream(); - std::unique_ptr buffer = stream->createBuffer(index); - if (!buffer) { - LOG(V4L2Compat, Error) << "Can't create buffer"; - return -ENOMEM; - } - std::unique_ptr request = - std::unique_ptr(camera_->createRequest()); + std::unique_ptr(camera_->createRequest(index)); if (!request) { LOG(V4L2Compat, Error) << "Can't create request"; return -ENOMEM; } - int ret = request->addBuffer(stream, std::move(buffer)); + Stream *stream = config_->at(0).stream(); + FrameBuffer *buffer = bufferAllocator_->buffers(stream)[index].get(); + int ret = request->addBuffer(stream, buffer); if (ret < 0) { LOG(V4L2Compat, Error) << "Can't set buffer for request"; return -ENOMEM; diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h index 06eab0e1c93b7c8a..f1f04d9ef6edf5e4 100644 --- a/src/v4l2/v4l2_camera.h +++ b/src/v4l2/v4l2_camera.h @@ -9,50 +9,38 @@ #define __V4L2_CAMERA_H__ #include -#include #include +#include #include #include #include +#include #include "semaphore.h" using namespace libcamera; -class V4L2FrameMetadata +class V4L2Camera : public Object { public: - V4L2FrameMetadata(Buffer *buffer); - - int index() const { return index_; } - - unsigned int bytesused() const { return bytesused_; } - uint64_t timestamp() const { return timestamp_; } - unsigned int sequence() const { return sequence_; } - - FrameMetadata::Status status() const { return status_; } - -private: - int index_; + struct Buffer { + Buffer(unsigned int index, const FrameMetadata &data) + : index(index), data(data) + { + } - unsigned int bytesused_; - uint64_t timestamp_; - unsigned int sequence_; + unsigned int index; + FrameMetadata data; + }; - FrameMetadata::Status status_; -}; - -class V4L2Camera : public Object -{ -public: V4L2Camera(std::shared_ptr camera); ~V4L2Camera(); int open(); void close(); void getStreamConfig(StreamConfiguration *streamConfig); - std::vector completedBuffers(); + std::vector completedBuffers(); int configure(StreamConfiguration *streamConfigOut, const Size &size, PixelFormat pixelformat, @@ -78,9 +66,10 @@ private: bool isRunning_; std::mutex bufferLock_; + FrameBufferAllocator *bufferAllocator_; std::deque> pendingRequests_; - std::deque> completedBuffers_; + std::deque> completedBuffers_; }; #endif /* __V4L2_CAMERA_H__ */ diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp index 59cf360edd28f69c..d76570a926cf667b 100644 --- a/src/v4l2/v4l2_camera_proxy.cpp +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -187,17 +187,18 @@ void V4L2CameraProxy::querycap(std::shared_ptr camera) void V4L2CameraProxy::updateBuffers() { - std::vector completedBuffers = vcam_->completedBuffers(); - for (V4L2FrameMetadata &fmd : completedBuffers) { - struct v4l2_buffer &buf = buffers_[fmd.index()]; + std::vector completedBuffers = vcam_->completedBuffers(); + for (const V4L2Camera::Buffer &buffer : completedBuffers) { + const FrameMetadata &fmd = buffer.data; + struct v4l2_buffer &buf = buffers_[buffer.index]; - switch (fmd.status()) { + switch (fmd.status) { case FrameMetadata::FrameSuccess: - buf.bytesused = fmd.bytesused(); + buf.bytesused = fmd.planes[0].bytesused; buf.field = V4L2_FIELD_NONE; - buf.timestamp.tv_sec = fmd.timestamp() / 1000000000; - buf.timestamp.tv_usec = fmd.timestamp() % 1000000; - buf.sequence = fmd.sequence(); + buf.timestamp.tv_sec = fmd.timestamp / 1000000000; + buf.timestamp.tv_usec = fmd.timestamp % 1000000; + buf.sequence = fmd.sequence; buf.flags |= V4L2_BUF_FLAG_DONE; break; diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index 327db7912c09ed2d..f506d1b221e568ad 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -120,7 +120,7 @@ public: } protected: - void bufferComplete(Request *request, Buffer *buffer) + void bufferComplete(Request *request, FrameBuffer *buffer) { if (buffer->metadata().status != FrameMetadata::FrameSuccess) return; @@ -133,17 +133,16 @@ protected: if (request->status() != Request::RequestComplete) return; - const std::map &buffers = request->buffers(); + const std::map &buffers = request->buffers(); completeRequestsCount_++; /* Create a new request. */ Stream *stream = buffers.begin()->first; - int dmabuf = buffers.begin()->second->dmabufs()[0]; - std::unique_ptr buffer = stream->createBuffer({ dmabuf, -1, -1 }); + FrameBuffer *buffer = buffers.begin()->second; request = camera_->createRequest(); - request->addBuffer(stream, std::move(buffer)); + request->addBuffer(stream, buffer); camera_->queueRequest(request); } @@ -158,9 +157,6 @@ protected: return TestFail; } - StreamConfiguration &cfg = config_->at(0); - cfg.memoryType = ExternalMemory; - return TestPass; } @@ -191,17 +187,14 @@ protected: return TestFail; std::vector requests; - for (const std::unique_ptr &framebuffer : source.buffers()) { - int dmabuf = framebuffer->planes()[0].fd.fd(); - + for (const std::unique_ptr &buffer : source.buffers()) { Request *request = camera_->createRequest(); if (!request) { std::cout << "Failed to create request" << std::endl; return TestFail; } - std::unique_ptr buffer = stream->createBuffer({ dmabuf, -1, -1 }); - if (request->addBuffer(stream, std::move(buffer))) { + if (request->addBuffer(stream, buffer.get())) { std::cout << "Failed to associating buffer with request" << std::endl; return TestFail; } diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp index 0d9ffc476650f414..de879ee4eb1420a6 100644 --- a/test/camera/capture.cpp +++ b/test/camera/capture.cpp @@ -26,7 +26,7 @@ protected: unsigned int completeBuffersCount_; unsigned int completeRequestsCount_; - void bufferComplete(Request *request, Buffer *buffer) + void bufferComplete(Request *request, FrameBuffer *buffer) { if (buffer->metadata().status != FrameMetadata::FrameSuccess) return; @@ -39,17 +39,16 @@ protected: if (request->status() != Request::RequestComplete) return; - const std::map &buffers = request->buffers(); + const std::map &buffers = request->buffers(); completeRequestsCount_++; /* Create a new request. */ Stream *stream = buffers.begin()->first; - Buffer *buffer = buffers.begin()->second; - std::unique_ptr newBuffer = stream->createBuffer(buffer->index()); + FrameBuffer *buffer = buffers.begin()->second; request = camera_->createRequest(); - request->addBuffer(stream, std::move(newBuffer)); + request->addBuffer(stream, buffer); camera_->queueRequest(request); } @@ -64,9 +63,16 @@ protected: return TestFail; } + allocator_ = FrameBufferAllocator::create(camera_); + return TestPass; } + void cleanup() override + { + delete allocator_; + } + int run() override { StreamConfiguration &cfg = config_->at(0); @@ -87,21 +93,20 @@ protected: } Stream *stream = cfg.stream(); + + int ret = allocator_->allocate(stream); + if (ret < 0) + return TestFail; + std::vector requests; - for (unsigned int i = 0; i < cfg.bufferCount; ++i) { + for (const std::unique_ptr &buffer : allocator_->buffers(stream)) { Request *request = camera_->createRequest(); if (!request) { cout << "Failed to create request" << endl; return TestFail; } - std::unique_ptr buffer = stream->createBuffer(i); - if (!buffer) { - cout << "Failed to create buffer " << i << endl; - return TestFail; - } - - if (request->addBuffer(stream, std::move(buffer))) { + if (request->addBuffer(stream, buffer.get())) { cout << "Failed to associating buffer with request" << endl; return TestFail; } @@ -134,10 +139,12 @@ protected: while (timer.isRunning()) dispatcher->processEvents(); - if (completeRequestsCount_ <= cfg.bufferCount * 2) { + unsigned int nbuffers = allocator_->buffers(stream).size(); + + if (completeRequestsCount_ <= nbuffers * 2) { cout << "Failed to capture enough frames (got " << completeRequestsCount_ << " expected at least " - << cfg.bufferCount * 2 << ")" << endl; + << nbuffers * 2 << ")" << endl; return TestFail; } @@ -160,6 +167,7 @@ protected: } std::unique_ptr config_; + FrameBufferAllocator *allocator_; }; } /* namespace */ diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp index f627b8f37422350e..f3a7ca7c32a5ec97 100644 --- a/test/camera/statemachine.cpp +++ b/test/camera/statemachine.cpp @@ -185,6 +185,12 @@ protected: if (camera_->allocateBuffers()) return TestFail; + /* Use internally allocated buffers. */ + allocator_ = FrameBufferAllocator::create(camera_); + Stream *stream = *camera_->streams().begin(); + if (allocator_->allocate(stream) < 0) + return TestFail; + if (camera_->start()) return TestFail; @@ -218,8 +224,7 @@ protected: return TestFail; Stream *stream = *camera_->streams().begin(); - std::unique_ptr buffer = stream->createBuffer(0); - if (request->addBuffer(stream, std::move(buffer))) + if (request->addBuffer(stream, allocator_->buffers(stream)[0].get())) return TestFail; if (camera_->queueRequest(request)) @@ -229,6 +234,8 @@ protected: if (camera_->stop()) return TestFail; + delete allocator_; + if (camera_->freeBuffers()) return TestFail; @@ -283,6 +290,7 @@ protected: } std::unique_ptr defconf_; + FrameBufferAllocator *allocator_; }; } /* namespace */ From patchwork Sun Jan 12 01:02:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2609 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 51929606E9 for ; Sun, 12 Jan 2020 02:03:26 +0100 (CET) X-Halon-ID: 53c14987-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 53c14987-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:21 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:07 +0100 Message-Id: <20200112010212.2609025-28-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 27/32] libcamera: v4l2_videodevice: Remove Buffer interface X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:26 -0000 The Buffer interface is no longer in use and can be removed. While doing so clean up the two odd names (dequeueFrameBuffer() and queuedFrameBuffers_) that had to be used when adding the FrameBuffer interface. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- * Changes since v2 - Renamed createFrameBuffer() to createBuffer() - Rename dequeuBuffer() to dequeueBuffer() --- src/libcamera/include/v4l2_videodevice.h | 21 +- src/libcamera/pipeline/ipu3/ipu3.cpp | 8 +- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 6 +- src/libcamera/pipeline/uvcvideo.cpp | 2 +- src/libcamera/pipeline/vimc.cpp | 2 +- src/libcamera/v4l2_videodevice.cpp | 339 +---------------------- test/v4l2_videodevice/buffer_sharing.cpp | 4 +- test/v4l2_videodevice/capture_async.cpp | 2 +- test/v4l2_videodevice/v4l2_m2mdevice.cpp | 4 +- 9 files changed, 33 insertions(+), 355 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index 09967d3c6ae59044..e4d35ab3ef360c6f 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -182,19 +182,13 @@ public: int setFormat(V4L2DeviceFormat *format); ImageFormats formats(); - int exportBuffers(BufferPool *pool); - int importBuffers(BufferPool *pool); int exportBuffers(unsigned int count, std::vector> *buffers); int importBuffers(unsigned int count); int releaseBuffers(); - int queueBuffer(Buffer *buffer); - std::vector> queueAllBuffers(); - Signal bufferReady; int queueBuffer(FrameBuffer *buffer); - /* todo Rename to bufferReady when the Buffer version is removed */ - Signal frameBufferReady; + Signal bufferReady; int streamOn(); int streamOff(); @@ -223,26 +217,19 @@ private: std::vector enumSizes(unsigned int pixelFormat); int requestBuffers(unsigned int count); - int createPlane(BufferMemory *buffer, unsigned int index, - unsigned int plane, unsigned int length); - std::unique_ptr createFrameBuffer(const struct v4l2_buffer &buf); + std::unique_ptr createBuffer(const struct v4l2_buffer &buf); FileDescriptor exportDmabufFd(unsigned int index, unsigned int plane); - Buffer *dequeueBuffer(); void bufferAvailable(EventNotifier *notifier); - /* todo Rename to dequeueBuffer() when the Buffer version is removed */ - FrameBuffer *dequeueFrameBuffer(); + FrameBuffer *dequeueBuffer(); V4L2Capability caps_; enum v4l2_buf_type bufferType_; enum v4l2_memory memoryType_; - BufferPool *bufferPool_; V4L2BufferCache *cache_; - std::map queuedBuffers_; - /* todo Rename to queuedBuffers_ when the Buffer version is removed */ - std::map queuedFrameBuffers_; + std::map queuedBuffers_; EventNotifier *fdEvent_; }; diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 065f5d980b68e1cf..1ea4d938ad88ae21 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -903,13 +903,13 @@ int PipelineHandlerIPU3::registerCameras() * associated ImgU input where they get processed and * returned through the ImgU main and secondary outputs. */ - data->cio2_.output_->frameBufferReady.connect(data.get(), + data->cio2_.output_->bufferReady.connect(data.get(), &IPU3CameraData::cio2BufferReady); - data->imgu_->input_->frameBufferReady.connect(data.get(), + data->imgu_->input_->bufferReady.connect(data.get(), &IPU3CameraData::imguInputBufferReady); - data->imgu_->output_.dev->frameBufferReady.connect(data.get(), + data->imgu_->output_.dev->bufferReady.connect(data.get(), &IPU3CameraData::imguOutputBufferReady); - data->imgu_->viewfinder_.dev->frameBufferReady.connect(data.get(), + data->imgu_->viewfinder_.dev->bufferReady.connect(data.get(), &IPU3CameraData::imguOutputBufferReady); /* Create and register the Camera instance. */ diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index d669d2ded89607d2..da6e079f51620234 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -950,9 +950,9 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator) if (param_->open() < 0) return false; - video_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady); - stat_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::statReady); - param_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::paramReady); + video_->bufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady); + stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady); + param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady); /* Configure default links. */ if (initLinks() < 0) { diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index 66380e29e1a78e13..67d29b7919e46801 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -352,7 +352,7 @@ int UVCCameraData::init(MediaEntity *entity) if (ret) return ret; - video_->frameBufferReady.connect(this, &UVCCameraData::bufferReady); + video_->bufferReady.connect(this, &UVCCameraData::bufferReady); /* Initialise the supported controls. */ const ControlInfoMap &controls = video_->controls(); diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 5e0f5c63b7fbe86a..4cfdb5ae79ce5779 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -437,7 +437,7 @@ int VimcCameraData::init(MediaDevice *media) if (video_->open()) return -ENODEV; - video_->frameBufferReady.connect(this, &VimcCameraData::bufferReady); + video_->bufferReady.connect(this, &VimcCameraData::bufferReady); raw_ = new V4L2VideoDevice(media->getEntityByName("Raw Capture 1")); if (raw_->open()) diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index ed5b2377b806f19c..f00da22f1caa4105 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -410,8 +410,7 @@ const std::string V4L2DeviceFormat::toString() const * \param[in] deviceNode The file-system path to the video device node */ V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode) - : V4L2Device(deviceNode), bufferPool_(nullptr), cache_(nullptr), - fdEvent_(nullptr) + : V4L2Device(deviceNode), cache_(nullptr), fdEvent_(nullptr) { /* * We default to an MMAP based CAPTURE video device, however this will @@ -970,112 +969,6 @@ int V4L2VideoDevice::requestBuffers(unsigned int count) return 0; } -/** - * \brief Request buffers to be allocated from the video device and stored in - * the buffer pool provided. - * \param[out] pool BufferPool to populate with buffers - * \return 0 on success or a negative error code otherwise - */ -int V4L2VideoDevice::exportBuffers(BufferPool *pool) -{ - unsigned int i; - int ret; - - memoryType_ = V4L2_MEMORY_MMAP; - - ret = requestBuffers(pool->count()); - if (ret) - return ret; - - /* Map the buffers. */ - for (i = 0; i < pool->count(); ++i) { - struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; - struct v4l2_buffer buf = {}; - BufferMemory &buffer = pool->buffers()[i]; - - buf.index = i; - buf.type = bufferType_; - buf.memory = memoryType_; - buf.length = VIDEO_MAX_PLANES; - buf.m.planes = planes; - - ret = ioctl(VIDIOC_QUERYBUF, &buf); - if (ret < 0) { - 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, i, p, - buf.m.planes[p].length); - if (ret) - break; - } - } else { - ret = createPlane(&buffer, i, 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 V4L2VideoDevice::createPlane(BufferMemory *buffer, unsigned int index, - unsigned int planeIndex, unsigned int length) -{ - LOG(V4L2, Debug) - << "Buffer " << index - << " plane " << planeIndex - << ": length=" << length; - - FileDescriptor fd = exportDmabufFd(index, planeIndex); - if (!fd.isValid()) - return -EINVAL; - - FrameBuffer::Plane plane; - plane.fd = fd; - plane.length = length; - buffer->planes().push_back(plane); - - return 0; -} - -/** - * \brief Import the externally allocated \a pool of buffers - * \param[in] pool BufferPool of buffers to import - * \return 0 on success or a negative error code otherwise - */ -int V4L2VideoDevice::importBuffers(BufferPool *pool) -{ - int ret; - - memoryType_ = V4L2_MEMORY_DMABUF; - - ret = requestBuffers(pool->count()); - if (ret) - return ret; - - LOG(V4L2, Debug) << "provided pool of " << pool->count() << " buffers"; - bufferPool_ = pool; - - return 0; -} - /** * \brief Allocate buffers from the video device * \param[in] count Number of buffers to allocate @@ -1114,7 +1007,7 @@ int V4L2VideoDevice::exportBuffers(unsigned int count, goto err_buf; } - std::unique_ptr buffer = createFrameBuffer(buf); + std::unique_ptr buffer = createBuffer(buf); if (!buffer) { LOG(V4L2, Error) << "Unable to create buffer"; ret = -EINVAL; @@ -1137,7 +1030,7 @@ err_buf: } std::unique_ptr -V4L2VideoDevice::createFrameBuffer(const struct v4l2_buffer &buf) +V4L2VideoDevice::createBuffer(const struct v4l2_buffer &buf) { const bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type); const unsigned int numPlanes = multiPlanar ? buf.length : 1; @@ -1217,127 +1110,12 @@ int V4L2VideoDevice::releaseBuffers() { LOG(V4L2, Debug) << "Releasing buffers"; - bufferPool_ = nullptr; delete cache_; cache_ = nullptr; return requestBuffers(0); } -/** - * \brief Queue a buffer into the video device - * \param[in] buffer The buffer to be queued - * - * For capture video devices the \a buffer will be filled with data by the - * device. For output video 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. - * - * \return 0 on success or a negative error code otherwise - */ -int V4L2VideoDevice::queueBuffer(Buffer *buffer) -{ - struct v4l2_plane v4l2Planes[VIDEO_MAX_PLANES] = {}; - struct v4l2_buffer buf = {}; - int ret; - - buf.index = buffer->index(); - buf.type = bufferType_; - buf.memory = memoryType_; - buf.field = V4L2_FIELD_NONE; - - bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type); - BufferMemory *mem = &bufferPool_->buffers()[buf.index]; - const std::vector &planes = mem->planes(); - - if (buf.memory == V4L2_MEMORY_DMABUF) { - if (multiPlanar) { - for (unsigned int p = 0; p < planes.size(); ++p) - v4l2Planes[p].m.fd = planes[p].fd.fd(); - } else { - buf.m.fd = planes[0].fd.fd(); - } - } - - if (multiPlanar) { - buf.length = planes.size(); - buf.m.planes = v4l2Planes; - } - - if (V4L2_TYPE_IS_OUTPUT(buf.type)) { - const FrameMetadata &metadata = buffer->metadata(); - - if (!metadata.planes.empty()) - buf.bytesused = metadata.planes[0].bytesused; - buf.sequence = metadata.sequence; - buf.timestamp.tv_sec = metadata.timestamp / 1000000000; - buf.timestamp.tv_usec = (metadata.timestamp / 1000) % 1000000; - } - - LOG(V4L2, Debug) << "Queueing buffer " << buf.index; - - ret = ioctl(VIDIOC_QBUF, &buf); - if (ret < 0) { - LOG(V4L2, Error) - << "Failed to queue buffer " << buf.index << ": " - << strerror(-ret); - return ret; - } - - if (queuedBuffers_.empty()) - fdEvent_->setEnabled(true); - - queuedBuffers_[buf.index] = buffer; - - return 0; -} - -/** - * \brief Queue all buffers into the video device - * - * When starting video capture users of the video device often need to queue - * all allocated buffers to the device. This helper method simplifies the - * implementation of the user by queuing all buffers and returning a vector of - * Buffer instances for each queued buffer. - * - * This method is meant to be used with video capture devices internal to a - * pipeline handler, such as ISP statistics capture devices, or raw CSI-2 - * receivers. For video capture devices facing applications, buffers shall - * instead be queued when requests are received, and for video output devices, - * buffers shall be queued when frames are ready to be output. - * - * The caller shall ensure that the returned buffers vector remains valid until - * all the queued buffers are dequeued, either during capture, or by stopping - * the video device. - * - * Calling this method on an output device or on a device that has buffers - * already queued is an error and will return an empty vector. - * - * \return A vector of queued buffers, which will be empty if an error occurs - */ -std::vector> V4L2VideoDevice::queueAllBuffers() -{ - int ret; - - if (!queuedBuffers_.empty()) - return {}; - - if (V4L2_TYPE_IS_OUTPUT(bufferType_)) - return {}; - - std::vector> buffers; - - for (unsigned int i = 0; i < bufferPool_->count(); ++i) { - Buffer *buffer = new Buffer(i); - buffers.emplace_back(buffer); - ret = queueBuffer(buffer); - if (ret) - return {}; - } - - return buffers; -} - /** * \brief Queue a buffer to the video device * \param[in] buffer The buffer to be queued @@ -1414,66 +1192,14 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer) return ret; } - if (queuedFrameBuffers_.empty()) + if (queuedBuffers_.empty()) fdEvent_->setEnabled(true); - queuedFrameBuffers_[buf.index] = buffer; + queuedBuffers_[buf.index] = buffer; return 0; } -/** - * \brief Dequeue the next available buffer from the video 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 success, or nullptr otherwise - */ -Buffer *V4L2VideoDevice::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(VIDIOC_DQBUF, &buf); - if (ret < 0) { - LOG(V4L2, Error) - << "Failed to dequeue buffer: " << strerror(-ret); - return nullptr; - } - - LOG(V4L2, Debug) << "Dequeuing buffer " << buf.index; - ASSERT(buf.index < bufferPool_->count()); - - auto it = queuedBuffers_.find(buf.index); - Buffer *buffer = it->second; - queuedBuffers_.erase(it); - - if (queuedBuffers_.empty()) - fdEvent_->setEnabled(false); - - buffer->index_ = buf.index; - - buffer->metadata_.status = buf.flags & V4L2_BUF_FLAG_ERROR - ? FrameMetadata::FrameError - : FrameMetadata::FrameSuccess; - buffer->metadata_.sequence = buf.sequence; - buffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL - + buf.timestamp.tv_usec * 1000ULL; - buffer->metadata_.planes = { { buf.bytesused } }; - - return buffer; -} - /** * \brief Slot to handle completed buffer events from the V4L2 video device * \param[in] notifier The event notifier @@ -1486,30 +1212,12 @@ Buffer *V4L2VideoDevice::dequeueBuffer() */ void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier) { - /* - * This is a hack which allows both Buffer and FrameBuffer interfaces - * to work with the same code base. This allows different parts of - * libcamera to migrate to FrameBuffer in different patches. - * - * If we have a cache_ we know FrameBuffer is in use. - * - * \todo Remove this hack when the Buffer interface is removed. - */ - if (cache_) { - FrameBuffer *buffer = dequeueFrameBuffer(); - if (!buffer) - return; - - /* Notify anyone listening to the device. */ - frameBufferReady.emit(buffer); - } else { - Buffer *buffer = dequeueBuffer(); - if (!buffer) - return; + FrameBuffer *buffer = dequeueBuffer(); + if (!buffer) + return; - /* Notify anyone listening to the device. */ - bufferReady.emit(buffer); - } + /* Notify anyone listening to the device. */ + bufferReady.emit(buffer); } /** @@ -1518,11 +1226,9 @@ void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier) * This method dequeues the next available buffer from the device. If no buffer * is available to be dequeued it will return nullptr immediately. * - * \todo Rename to dequeueBuffer() once the FrameBuffer transition is complete - * * \return A pointer to the dequeued buffer on success, or nullptr otherwise */ -FrameBuffer *V4L2VideoDevice::dequeueFrameBuffer() +FrameBuffer *V4L2VideoDevice::dequeueBuffer() { struct v4l2_buffer buf = {}; struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; @@ -1549,11 +1255,11 @@ FrameBuffer *V4L2VideoDevice::dequeueFrameBuffer() cache_->put(buf.index); - auto it = queuedFrameBuffers_.find(buf.index); + auto it = queuedBuffers_.find(buf.index); FrameBuffer *buffer = it->second; - queuedFrameBuffers_.erase(it); + queuedBuffers_.erase(it); - if (queuedFrameBuffers_.empty()) + if (queuedBuffers_.empty()) fdEvent_->setEnabled(false); buffer->metadata_.status = buf.flags & V4L2_BUF_FLAG_ERROR @@ -1576,11 +1282,6 @@ FrameBuffer *V4L2VideoDevice::dequeueFrameBuffer() /** * \var V4L2VideoDevice::bufferReady - * \brief A Signal emitted when a buffer completes - */ - -/** - * \var V4L2VideoDevice::frameBufferReady * \brief A Signal emitted when a framebuffer completes */ @@ -1625,23 +1326,13 @@ int V4L2VideoDevice::streamOff() /* Send back all queued buffers. */ for (auto it : queuedBuffers_) { - unsigned int index = it.first; - Buffer *buffer = it.second; - - buffer->index_ = index; - buffer->cancel(); - bufferReady.emit(buffer); - } - - for (auto it : queuedFrameBuffers_) { FrameBuffer *buffer = it.second; buffer->metadata_.status = FrameMetadata::FrameCancelled; - frameBufferReady.emit(buffer); + bufferReady.emit(buffer); } queuedBuffers_.clear(); - queuedFrameBuffers_.clear(); fdEvent_->setEnabled(false); return 0; diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp index 6acb06a24b47f653..fefa969a5f3926a2 100644 --- a/test/v4l2_videodevice/buffer_sharing.cpp +++ b/test/v4l2_videodevice/buffer_sharing.cpp @@ -120,8 +120,8 @@ protected: Timer timeout; int ret; - capture_->frameBufferReady.connect(this, &BufferSharingTest::captureBufferReady); - output_->frameBufferReady.connect(this, &BufferSharingTest::outputBufferReady); + capture_->bufferReady.connect(this, &BufferSharingTest::captureBufferReady); + output_->bufferReady.connect(this, &BufferSharingTest::outputBufferReady); for (const std::unique_ptr &buffer : buffers_) { if (capture_->queueBuffer(buffer.get())) { diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp index a57abed3bd0debc1..6a103a035f3d4635 100644 --- a/test/v4l2_videodevice/capture_async.cpp +++ b/test/v4l2_videodevice/capture_async.cpp @@ -42,7 +42,7 @@ protected: if (ret < 0) return TestFail; - capture_->frameBufferReady.connect(this, &CaptureAsyncTest::receiveBuffer); + capture_->bufferReady.connect(this, &CaptureAsyncTest::receiveBuffer); for (const std::unique_ptr &buffer : buffers_) { if (capture_->queueBuffer(buffer.get())) { diff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp index 43b99c4f7ea9bf26..203afc4fc0339e24 100644 --- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp +++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp @@ -124,8 +124,8 @@ protected: return TestFail; } - capture->frameBufferReady.connect(this, &V4L2M2MDeviceTest::receiveCaptureBuffer); - output->frameBufferReady.connect(this, &V4L2M2MDeviceTest::outputBufferComplete); + capture->bufferReady.connect(this, &V4L2M2MDeviceTest::receiveCaptureBuffer); + output->bufferReady.connect(this, &V4L2M2MDeviceTest::outputBufferComplete); for (const std::unique_ptr &buffer : captureBuffers_) { if (capture->queueBuffer(buffer.get())) { From patchwork Sun Jan 12 01:02:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2610 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BFCB0606E4 for ; Sun, 12 Jan 2020 02:03:27 +0100 (CET) X-Halon-ID: 549faaf0-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 549faaf0-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:23 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:08 +0100 Message-Id: <20200112010212.2609025-29-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 28/32] libcamera: Remove dead code after switch to FrameBuffer X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:28 -0000 Delete all dead code after switching to the FrameBuffer interface. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- include/libcamera/buffer.h | 61 --------- include/libcamera/stream.h | 23 ---- src/android/camera_device.cpp | 1 - src/libcamera/buffer.cpp | 184 ------------------------- src/libcamera/stream.cpp | 250 +--------------------------------- 5 files changed, 2 insertions(+), 517 deletions(-) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index 0eb84c32cc8570c5..8e5ec699e3925eee 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -7,7 +7,6 @@ #ifndef __LIBCAMERA_BUFFER_H__ #define __LIBCAMERA_BUFFER_H__ -#include #include #include @@ -16,7 +15,6 @@ namespace libcamera { class Request; -class Stream; struct FrameMetadata { enum Status { @@ -71,65 +69,6 @@ private: unsigned int cookie_; }; -class BufferMemory final -{ -public: - const std::vector &planes() const { return planes_; } - std::vector &planes() { return planes_; } - -private: - 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_; -}; - -class Buffer final -{ -public: - Buffer(unsigned int index = -1, const Buffer *metadata = nullptr); - Buffer(const Buffer &) = delete; - Buffer &operator=(const Buffer &) = delete; - - unsigned int index() const { return index_; } - const std::array &dmabufs() const { return dmabuf_; } - BufferMemory *mem() { return mem_; } - - const FrameMetadata &metadata() const { return metadata_; }; - - Request *request() const { return request_; } - Stream *stream() const { return stream_; } - -private: - friend class Camera; - friend class Request; - friend class Stream; - friend class V4L2VideoDevice; - - void cancel(); - - unsigned int index_; - std::array dmabuf_; - BufferMemory *mem_; - - FrameMetadata metadata_; - - Request *request_; - Stream *stream_; -}; - } /* namespace libcamera */ #endif /* __LIBCAMERA_BUFFER_H__ */ diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index a404eccf34d9c93b..29a8030dff71d58f 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -36,11 +36,6 @@ private: std::map> formats_; }; -enum MemoryType { - InternalMemory, - ExternalMemory, -}; - struct StreamConfiguration { StreamConfiguration(); StreamConfiguration(const StreamFormats &formats); @@ -48,7 +43,6 @@ struct StreamConfiguration { PixelFormat pixelFormat; Size size; - MemoryType memoryType; unsigned int bufferCount; Stream *stream() const { return stream_; } @@ -75,29 +69,12 @@ class Stream public: Stream(); - std::unique_ptr createBuffer(unsigned int index); - std::unique_ptr createBuffer(const std::array &fds); - - BufferPool &bufferPool() { return bufferPool_; } - std::vector &buffers() { return bufferPool_.buffers(); } const StreamConfiguration &configuration() const { return configuration_; } - MemoryType memoryType() const { return memoryType_; } protected: friend class Camera; - int mapBuffer(const Buffer *buffer); - void unmapBuffer(const Buffer *buffer); - - void createBuffers(MemoryType memory, unsigned int count); - void destroyBuffers(); - - BufferPool bufferPool_; StreamConfiguration configuration_; - MemoryType memoryType_; - -private: - std::vector, unsigned int>> bufferCache_; }; } /* namespace libcamera */ diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index ab3e44889f8b7ada..49321db07a2c93d5 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -641,7 +641,6 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list) StreamConfiguration *streamConfiguration = &config_->at(0); streamConfiguration->size.width = camera3Stream->width; streamConfiguration->size.height = camera3Stream->height; - streamConfiguration->memoryType = ExternalMemory; /* * \todo We'll need to translate from Android defined pixel format codes diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp index 92ac28387bd4c4f7..673a63d3d1658190 100644 --- a/src/libcamera/buffer.cpp +++ b/src/libcamera/buffer.cpp @@ -96,190 +96,6 @@ LOG_DEFINE_CATEGORY(Buffer) * \brief Array of per-plane metadata */ -/** - * \class BufferMemory - * \brief A memory buffer to store an image - * - * The BufferMemory class represents the memory buffers used to store full frame - * images, which may contain multiple separate memory Plane objects if the - * image format is multi-planar. - */ - -/** - * \fn BufferMemory::planes() const - * \brief Retrieve the planes within the buffer - * \return A const reference to a vector holding all planes within the buffer - */ - -/** - * \fn BufferMemory::planes() - * \brief Retrieve the planes within the buffer - * \return A reference to a vector holding all planes within the buffer - */ - -/** - * \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) -{ - buffers_.resize(count); -} - -/** - * \brief Release all buffers from pool - * - * If no buffers have been created or if buffers have already been released no - * operation is performed. - */ -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. - */ - -/** - * \class Buffer - * \brief A buffer handle and dynamic metadata - * - * The Buffer class references a buffer memory and associates dynamic metadata - * related to the frame contained in the buffer. It allows referencing buffer - * memory through a single interface regardless of whether the memory is - * allocated internally in libcamera or provided externally through dmabuf. - * - * Buffer instances are allocated dynamically for a stream through - * Stream::createBuffer(), added to a request with Request::addBuffer() and - * deleted automatically after the request complete handler returns. - */ - -/** - * \brief Construct a buffer not associated with any stream - * - * This method constructs an orphaned buffer not associated with any stream. It - * is not meant to be called by applications, they should instead create buffers - * for a stream with Stream::createBuffer(). - */ -Buffer::Buffer(unsigned int index, const Buffer *metadata) - : index_(index), dmabuf_({ -1, -1, -1 }), request_(nullptr), - stream_(nullptr) -{ - if (metadata) - metadata_ = metadata->metadata(); - else - metadata_ = {}; - - metadata_.status = FrameMetadata::FrameSuccess; -} - -/** - * \fn Buffer::index() - * \brief Retrieve the Buffer index - * \return The buffer index - */ - -/** - * \fn Buffer::dmabufs() - * \brief Retrieve the dmabuf file descriptors for all buffer planes - * - * The dmabufs array contains one dmabuf file descriptor per plane. Unused - * entries are set to -1. - * - * \return The dmabuf file descriptors - */ - -/** - * \fn Buffer::mem() - * \brief Retrieve the BufferMemory this buffer is associated with - * - * The association between the buffer and a BufferMemory instance is valid from - * the time the request containing this buffer is queued to a camera to the end - * of that request's completion handler. - * - * \return The BufferMemory this buffer is associated with - */ - -/** - * \fn Buffer::metadata() - * \brief Retrieve the buffer metadata - * - * The buffer metadata is updated when the buffer contents are modified, for - * example when a frame has been captured to the buffer by the hardware. - * - * \return Metadata for the buffer - */ - -/** - * \fn Buffer::request() - * \brief Retrieve the request this buffer belongs to - * - * The intended callers of this method are buffer completion handlers that - * need to associate a buffer to the request it belongs to. - * - * A Buffer is associated to a request by Request::addBuffer() and the - * association is valid until the buffer completes. The returned request - * pointer is valid only during that interval. - * - * \return The Request the Buffer belongs to, or nullptr if the buffer is - * either completed or not associated with a request - */ - -/** - * \fn Buffer::stream() - * \brief Retrieve the stream this buffer is associated with - * - * A Buffer is associated to the stream that created it with - * Stream::createBuffer() and the association is valid until the buffer is - * destroyed. Buffer instances that are created directly are not associated - * with any stream. - * - * \return The Stream the Buffer is associated with, or nullptr if the buffer - * is not associated with a stream - */ - -/** - * \brief Mark a buffer as cancel by setting its status to BufferCancelled - */ -void Buffer::cancel() -{ - metadata_.status = FrameMetadata::FrameCancelled; - metadata_.sequence = 0; - metadata_.timestamp = 0; - metadata_.planes = {}; -} - -/** - * \var Buffer::request_ - * \brief The request this buffer belongs to - * - * This member is intended to be set by Request::addBuffer() and - * Request::completeBuffer(). - */ - /** * \class FrameBuffer * \brief Frame buffer data and its associated dynamic metadata diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index 16a323f135b2022a..13789e9eb344f95c 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -266,17 +266,6 @@ SizeRange StreamFormats::range(PixelFormat pixelformat) const return range; } -/** - * \enum MemoryType - * \brief Define the memory type used by a Stream - * \var MemoryType::InternalMemory - * The Stream uses memory allocated internally by the library and exported to - * applications. - * \var MemoryType::ExternalMemory - * The Stream uses memory allocated externally by application and imported in - * the library. - */ - /** * \struct StreamConfiguration * \brief Configuration parameters for a stream @@ -290,7 +279,7 @@ SizeRange StreamFormats::range(PixelFormat pixelformat) const * handlers provied StreamFormats. */ StreamConfiguration::StreamConfiguration() - : pixelFormat(0), memoryType(InternalMemory), stream_(nullptr) + : pixelFormat(0), stream_(nullptr) { } @@ -298,8 +287,7 @@ StreamConfiguration::StreamConfiguration() * \brief Construct a configuration with stream formats */ StreamConfiguration::StreamConfiguration(const StreamFormats &formats) - : pixelFormat(0), memoryType(InternalMemory), stream_(nullptr), - formats_(formats) + : pixelFormat(0), stream_(nullptr), formats_(formats) { } @@ -313,11 +301,6 @@ StreamConfiguration::StreamConfiguration(const StreamFormats &formats) * \brief Stream pixel format */ -/** - * \var StreamConfiguration::memoryType - * \brief The memory type the stream shall use - */ - /** * \var StreamConfiguration::bufferCount * \brief Requested number of buffers to allocate for the stream @@ -420,236 +403,12 @@ Stream::Stream() { } -/** - * \brief Create a Buffer instance referencing the memory buffer \a index - * \param[in] index The desired buffer index - * - * This method creates a Buffer instance that references a BufferMemory from - * the stream's buffers pool by its \a index. The index shall be lower than the - * number of buffers in the pool. - * - * This method is only valid for streams that use the InternalMemory type. It - * will return a null pointer when called on streams using the ExternalMemory - * type. - * - * \return A newly created Buffer on success or nullptr otherwise - */ -std::unique_ptr Stream::createBuffer(unsigned int index) -{ - if (memoryType_ != InternalMemory) { - LOG(Stream, Error) << "Invalid stream memory type"; - return nullptr; - } - - if (index >= bufferPool_.count()) { - LOG(Stream, Error) << "Invalid buffer index " << index; - return nullptr; - } - - Buffer *buffer = new Buffer(); - buffer->index_ = index; - buffer->stream_ = this; - - return std::unique_ptr(buffer); -} - -/** - * \brief Create a Buffer instance that represents a memory area identified by - * dmabuf file descriptors - * \param[in] fds The dmabuf file descriptors for each plane - * - * This method creates a Buffer instance that references buffer memory - * allocated outside of libcamera through dmabuf file descriptors. The \a - * dmabuf array shall contain a file descriptor for each plane in the buffer, - * and unused entries shall be set to -1. - * - * The buffer is created without a valid index, as it does not yet map to any of - * the stream's BufferMemory instances. An index will be assigned at the time - * the buffer is queued to the camera in a request. Applications may thus - * create any number of Buffer instances, providing that no more than the - * number of buffers allocated for the stream are queued at any given time. - * - * This method is only valid for streams that use the ExternalMemory type. It - * will return a null pointer when called on streams using the InternalMemory - * type. - * - * \sa Stream::mapBuffer() - * - * \return A newly created Buffer on success or nullptr otherwise - */ -std::unique_ptr Stream::createBuffer(const std::array &fds) -{ - if (memoryType_ != ExternalMemory) { - LOG(Stream, Error) << "Invalid stream memory type"; - return nullptr; - } - - Buffer *buffer = new Buffer(); - buffer->dmabuf_ = fds; - buffer->stream_ = this; - - return std::unique_ptr(buffer); -} - -/** - * \fn Stream::bufferPool() - * \brief Retrieve the buffer pool for the stream - * - * The buffer pool handles the memory buffers used to store frames for the - * stream. It is initially created empty and shall be populated with - * buffers before being used. - * - * \return A reference to the buffer pool - */ - -/** - * \fn Stream::buffers() - * \brief Retrieve the memory buffers in the Stream's buffer pool - * \return The list of stream's memory buffers - */ - /** * \fn Stream::configuration() * \brief Retrieve the active configuration of the stream * \return The active configuration of the stream */ -/** - * \fn Stream::memoryType() - * \brief Retrieve the stream memory type - * \return The memory type used by the stream - */ - -/** - * \brief Map a Buffer to a buffer memory index - * \param[in] buffer The buffer to map to a buffer memory index - * - * Streams configured to use externally allocated memory need to maintain a - * best-effort association between the memory area the \a buffer represents - * and the associated buffer memory in the Stream's pool. - * - * The buffer memory to use, once the \a buffer reaches the video device, - * is selected using the index assigned to the \a buffer and to minimize - * relocations in the V4L2 back-end, this operation provides a best-effort - * caching mechanism that associates to the dmabuf file descriptors contained - * in the \a buffer the index of the buffer memory that was lastly queued with - * those file descriptors set. - * - * If the Stream uses internally allocated memory, the index of the memory - * buffer to use will match the one request at Stream::createBuffer(unsigned int) - * time, and no mapping is thus required. - * - * \return The buffer memory index for the buffer on success, or a negative - * error code otherwise - * \retval -ENOMEM No buffer memory was available to map the buffer - */ -int Stream::mapBuffer(const Buffer *buffer) -{ - ASSERT(memoryType_ == ExternalMemory); - - if (bufferCache_.empty()) - return -ENOMEM; - - const std::array &dmabufs = buffer->dmabufs(); - - /* - * Try to find a previously mapped buffer in the cache. If we miss, use - * the oldest entry in the cache. - */ - auto map = std::find_if(bufferCache_.begin(), bufferCache_.end(), - [&](std::pair, unsigned int> &entry) { - return entry.first == dmabufs; - }); - if (map == bufferCache_.end()) - map = bufferCache_.begin(); - - /* - * Update the dmabuf file descriptors of the entry. We can't assume that - * identical file descriptor numbers refer to the same dmabuf object as - * it may have been closed and its file descriptor reused. We thus need - * to update the plane's internally cached mmap()ed memory. - */ - unsigned int index = map->second; - BufferMemory *mem = &bufferPool_.buffers()[index]; - mem->planes().clear(); - - for (unsigned int i = 0; i < dmabufs.size(); ++i) { - if (dmabufs[i] == -1) - break; - - FrameBuffer::Plane plane; - plane.fd = FileDescriptor(dmabufs[i]); - plane.length = 0; - mem->planes().push_back(plane); - } - - /* Remove the buffer from the cache and return its index. */ - bufferCache_.erase(map); - return index; -} - -/** - * \brief Unmap a Buffer from its buffer memory - * \param[in] buffer The buffer to unmap - * - * This method releases the buffer memory entry that was mapped by mapBuffer(), - * making it available for new mappings. - */ -void Stream::unmapBuffer(const Buffer *buffer) -{ - ASSERT(memoryType_ == ExternalMemory); - - bufferCache_.emplace_back(buffer->dmabufs(), buffer->index()); -} - -/** - * \brief Create buffers for the stream - * \param[in] count The number of buffers to create - * \param[in] memory The stream memory type - * - * Create \a count empty buffers in the Stream's buffer pool. - */ -void Stream::createBuffers(MemoryType memory, unsigned int count) -{ - destroyBuffers(); - if (count == 0) - return; - - memoryType_ = memory; - bufferPool_.createBuffers(count); - - /* Streams with internal memory usage do not need buffer mapping. */ - if (memoryType_ == InternalMemory) - return; - - /* - * Prepare for buffer mapping by adding all buffer memory entries to the - * cache. - */ - bufferCache_.clear(); - for (unsigned int i = 0; i < bufferPool_.count(); ++i) - bufferCache_.emplace_back(std::array{ -1, -1, -1 }, i); -} - -/** - * \brief Destroy buffers in the stream - * - * If no buffers have been created or if buffers have already been destroyed no - * operation is performed. - */ -void Stream::destroyBuffers() -{ - bufferPool_.destroyBuffers(); -} - -/** - * \var Stream::bufferPool_ - * \brief The pool of buffers associated with the stream - * - * The stream buffer pool is populated by the Camera class after a successful - * stream configuration. - */ - /** * \var Stream::configuration_ * \brief The stream configuration @@ -659,9 +418,4 @@ void Stream::destroyBuffers() * next call to Camera::configure() regardless of if it includes the stream. */ -/** - * \var Stream::memoryType_ - * \brief The stream memory type - */ - } /* namespace libcamera */ From patchwork Sun Jan 12 01:02:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2611 Return-Path: Received: from vsp-unauthed02.binero.net (vsp-unauthed02.binero.net [195.74.38.227]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 733FF606FC for ; Sun, 12 Jan 2020 02:03:29 +0100 (CET) X-Halon-ID: 557afff6-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 557afff6-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:25 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:09 +0100 Message-Id: <20200112010212.2609025-30-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 29/32] cam: Cache buffer memory mapping X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:29 -0000 With the buffer allocator in use it's possible to cache the dmabuf memory mappings when starting the camera instead of mapping and unmapping them each time. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/cam/buffer_writer.cpp | 27 ++++++++++++++++++++++----- src/cam/buffer_writer.h | 5 +++++ src/cam/capture.cpp | 3 +++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp index 1d7366c87714cd91..c5a5eb46224ac3eb 100644 --- a/src/cam/buffer_writer.cpp +++ b/src/cam/buffer_writer.cpp @@ -22,6 +22,27 @@ BufferWriter::BufferWriter(const std::string &pattern) { } +BufferWriter::~BufferWriter() +{ + for (auto &iter : mappedBuffers_) { + void *memory = iter.second.first; + unsigned int length = iter.second.second; + munmap(memory, length); + } + mappedBuffers_.clear(); +} + +void BufferWriter::mapBuffer(FrameBuffer *buffer) +{ + for (const FrameBuffer::Plane &plane : buffer->planes()) { + void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, + plane.fd.fd(), 0); + + mappedBuffers_[plane.fd.fd()] = + std::make_pair(memory, plane.length); + } +} + int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName) { std::string filename; @@ -44,9 +65,7 @@ int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName) return -errno; for (const FrameBuffer::Plane &plane : buffer->planes()) { - /* \todo Once the FrameBuffer is done cache mapped memory. */ - void *data = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, - plane.fd.fd(), 0); + void *data = mappedBuffers_[plane.fd.fd()].first; unsigned int length = plane.length; ret = ::write(fd, data, length); @@ -61,8 +80,6 @@ int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName) << length << std::endl; break; } - - munmap(data, length); } close(fd); diff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h index 5917a7dfb5e28106..8c9b2436fdae4fc3 100644 --- a/src/cam/buffer_writer.h +++ b/src/cam/buffer_writer.h @@ -7,6 +7,7 @@ #ifndef __LIBCAMERA_BUFFER_WRITER_H__ #define __LIBCAMERA_BUFFER_WRITER_H__ +#include #include #include @@ -15,12 +16,16 @@ class BufferWriter { public: BufferWriter(const std::string &pattern = "frame-#.bin"); + ~BufferWriter(); + + void mapBuffer(libcamera::FrameBuffer *buffer); int write(libcamera::FrameBuffer *buffer, const std::string &streamName); private: std::string pattern_; + std::map> mappedBuffers_; }; #endif /* __LIBCAMERA_BUFFER_WRITER_H__ */ diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp index dd078eb0ae4a2c62..738fa1c267eb6e36 100644 --- a/src/cam/capture.cpp +++ b/src/cam/capture.cpp @@ -116,6 +116,9 @@ int Capture::capture(EventLoop *loop, FrameBufferAllocator *allocator) << std::endl; return ret; } + + if (writer_) + writer_->mapBuffer(buffer.get()); } requests.push_back(request); From patchwork Sun Jan 12 01:02:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2612 Return-Path: Received: from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net [195.74.38.229]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 179AE606FC for ; Sun, 12 Jan 2020 02:03:30 +0100 (CET) X-Halon-ID: 56806cbd-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 56806cbd-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:26 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:10 +0100 Message-Id: <20200112010212.2609025-31-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 30/32] qcam: Cache buffer memory mapping X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:30 -0000 With the buffer allocator in use it's possible to cache the dmabuf memory mappings when starting the camera instead of mapping and unmapping them each time. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/qcam/main_window.cpp | 28 ++++++++++++++++++++++------ src/qcam/main_window.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 701a2b9a73d53d96..047bf15ea7c188f7 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -201,6 +201,13 @@ int MainWindow::startCapture() } requests.push_back(request); + + /* Map memory buffers and cache the mappings. */ + const FrameBuffer::Plane &plane = buffer->planes().front(); + void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, + plane.fd.fd(), 0); + mappedBuffers_[plane.fd.fd()] = + std::make_pair(memory, plane.length); } titleTimer_.start(2000); @@ -230,6 +237,13 @@ error: for (Request *request : requests) delete request; + for (auto &iter : mappedBuffers_) { + void *memory = iter.second.first; + unsigned int length = iter.second.second; + munmap(memory, length); + } + mappedBuffers_.clear(); + camera_->freeBuffers(); return ret; } @@ -243,6 +257,13 @@ void MainWindow::stopCapture() if (ret) std::cout << "Failed to stop capture" << std::endl; + for (auto &iter : mappedBuffers_) { + void *memory = iter.second.first; + unsigned int length = iter.second.second; + munmap(memory, length); + } + mappedBuffers_.clear(); + camera_->freeBuffers(); isCapturing_ = false; @@ -297,15 +318,10 @@ int MainWindow::display(FrameBuffer *buffer) if (buffer->planes().size() != 1) return -EINVAL; - /* \todo Once the FrameBuffer is done cache mapped memory. */ const FrameBuffer::Plane &plane = buffer->planes().front(); - void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, - plane.fd.fd(), 0); - + void *memory = mappedBuffers_[plane.fd.fd()].first; unsigned char *raw = static_cast(memory); viewfinder_->display(raw, buffer->metadata().planes[0].bytesused); - munmap(memory, plane.length); - return 0; } diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 05cde4ceab5f7ea1..04fb9e3ea869c3fb 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -71,6 +71,7 @@ private: uint32_t framesCaptured_; ViewFinder *viewfinder_; + std::map> mappedBuffers_; }; #endif /* __QCAM_MAIN_WINDOW__ */ From patchwork Sun Jan 12 01:02:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2613 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1BF9060705 for ; Sun, 12 Jan 2020 02:03:31 +0100 (CET) X-Halon-ID: 56df4051-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 56df4051-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:27 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:11 +0100 Message-Id: <20200112010212.2609025-32-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 31/32] libcamera: pipeline: Remove explicit buffer handling X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:31 -0000 With the FrameBuffer interface in place there is no need for the Camera to call into the specific pipelines allocation and freeing of buffers as it no longer needs to be synchronized with buffer allocation by the application. Remove the function prototypes in the pipeline handler base class and fold the functionality in the pipelines start() and stop() functions where needed. A follow up patch will remove the now no-op Camera::allocateBuffers() and Camera::freeBuffers(). Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- src/libcamera/camera.cpp | 8 +------ src/libcamera/include/pipeline_handler.h | 5 ---- src/libcamera/pipeline/ipu3/ipu3.cpp | 24 ++++++++++++-------- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 24 ++++++++++++-------- src/libcamera/pipeline/uvcvideo.cpp | 17 -------------- src/libcamera/pipeline/vimc.cpp | 17 -------------- src/libcamera/pipeline_handler.cpp | 29 ------------------------ 7 files changed, 30 insertions(+), 94 deletions(-) diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 3fe40feb88be7324..f3a7578d0834a9d6 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -726,12 +726,6 @@ int Camera::allocateBuffers() return -EINVAL; } - int ret = pipe_->allocateBuffers(this, activeStreams_); - if (ret) { - LOG(Camera, Error) << "Failed to allocate buffers"; - return ret; - } - state_ = CameraPrepared; return 0; @@ -752,7 +746,7 @@ int Camera::freeBuffers() state_ = CameraConfigured; - return pipe_->freeBuffers(this, activeStreams_); + return 0; } /** diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index 27f3852c6c87843f..a6c1e1fbae384405 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -75,11 +75,6 @@ public: virtual int importFrameBuffers(Camera *camera, Stream *stream) = 0; virtual void freeFrameBuffers(Camera *camera, Stream *stream) = 0; - virtual int allocateBuffers(Camera *camera, - const std::set &streams) = 0; - virtual int freeBuffers(Camera *camera, - const std::set &streams) = 0; - virtual int start(Camera *camera) = 0; virtual void stop(Camera *camera) = 0; diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 1ea4d938ad88ae21..7894084a025e9946 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -211,11 +211,6 @@ public: int importFrameBuffers(Camera *camera, Stream *stream) override; void freeFrameBuffers(Camera *camera, Stream *stream) override; - int allocateBuffers(Camera *camera, - const std::set &streams) override; - int freeBuffers(Camera *camera, - const std::set &streams) override; - int start(Camera *camera) override; void stop(Camera *camera) override; @@ -232,6 +227,9 @@ private: int registerCameras(); + int allocateBuffers(Camera *camera); + int freeBuffers(Camera *camera); + ImgUDevice imgu0_; ImgUDevice imgu1_; MediaDevice *cio2MediaDev_; @@ -652,8 +650,7 @@ void PipelineHandlerIPU3::freeFrameBuffers(Camera *camera, Stream *stream) * In order to be able to start the 'viewfinder' and 'stat' nodes, we need * memory to be reserved. */ -int PipelineHandlerIPU3::allocateBuffers(Camera *camera, - const std::set &streams) +int PipelineHandlerIPU3::allocateBuffers(Camera *camera) { IPU3CameraData *data = cameraData(camera); IPU3Stream *outStream = &data->outStream_; @@ -716,13 +713,12 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera, return 0; error: - freeBuffers(camera, streams); + freeBuffers(camera); return ret; } -int PipelineHandlerIPU3::freeBuffers(Camera *camera, - const std::set &streams) +int PipelineHandlerIPU3::freeBuffers(Camera *camera) { IPU3CameraData *data = cameraData(camera); @@ -739,6 +735,11 @@ int PipelineHandlerIPU3::start(Camera *camera) ImgUDevice *imgu = data->imgu_; int ret; + /* Allocate buffers for internal pipeline usage. */ + ret = allocateBuffers(camera); + if (ret) + return ret; + /* * Start the ImgU video devices, buffers will be queued to the * ImgU output and viewfinder when requests will be queued. @@ -757,6 +758,7 @@ int PipelineHandlerIPU3::start(Camera *camera) return 0; error: + freeBuffers(camera); LOG(IPU3, Error) << "Failed to start camera " << camera->name(); return ret; @@ -772,6 +774,8 @@ void PipelineHandlerIPU3::stop(Camera *camera) if (ret) LOG(IPU3, Warning) << "Failed to stop camera " << camera->name(); + + freeBuffers(camera); } int PipelineHandlerIPU3::queueRequestDevice(Camera *camera, Request *request) diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index da6e079f51620234..389a99cf52bd1cea 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -178,11 +178,6 @@ public: int importFrameBuffers(Camera *camera, Stream *stream) override; void freeFrameBuffers(Camera *camera, Stream *stream) override; - int allocateBuffers(Camera *camera, - const std::set &streams) override; - int freeBuffers(Camera *camera, - const std::set &streams) override; - int start(Camera *camera) override; void stop(Camera *camera) override; @@ -208,6 +203,9 @@ private: void paramReady(FrameBuffer *buffer); void statReady(FrameBuffer *buffer); + int allocateBuffers(Camera *camera); + int freeBuffers(Camera *camera); + MediaDevice *media_; V4L2Subdevice *dphy_; V4L2Subdevice *isp_; @@ -675,8 +673,7 @@ void PipelineHandlerRkISP1::freeFrameBuffers(Camera *camera, Stream *stream) video_->releaseBuffers(); } -int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, - const std::set &streams) +int PipelineHandlerRkISP1::allocateBuffers(Camera *camera) { RkISP1CameraData *data = cameraData(camera); unsigned int count = 1; @@ -720,8 +717,7 @@ error: return ret; } -int PipelineHandlerRkISP1::freeBuffers(Camera *camera, - const std::set &streams) +int PipelineHandlerRkISP1::freeBuffers(Camera *camera) { RkISP1CameraData *data = cameraData(camera); @@ -755,10 +751,16 @@ int PipelineHandlerRkISP1::start(Camera *camera) RkISP1CameraData *data = cameraData(camera); int ret; + /* Allocate buffers for internal pipeline usage. */ + ret = allocateBuffers(camera); + if (ret) + return ret; + data->frame_ = 0; ret = param_->streamOn(); if (ret) { + freeBuffers(camera); LOG(RkISP1, Error) << "Failed to start parameters " << camera->name(); return ret; @@ -767,6 +769,7 @@ int PipelineHandlerRkISP1::start(Camera *camera) ret = stat_->streamOn(); if (ret) { param_->streamOff(); + freeBuffers(camera); LOG(RkISP1, Error) << "Failed to start statistics " << camera->name(); return ret; @@ -776,6 +779,7 @@ int PipelineHandlerRkISP1::start(Camera *camera) if (ret) { param_->streamOff(); stat_->streamOff(); + freeBuffers(camera); LOG(RkISP1, Error) << "Failed to start camera " << camera->name(); @@ -820,6 +824,8 @@ void PipelineHandlerRkISP1::stop(Camera *camera) data->timeline_.reset(); + freeBuffers(camera); + activeCamera_ = nullptr; } diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index 67d29b7919e46801..47916ffb453672d4 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -70,11 +70,6 @@ public: int importFrameBuffers(Camera *camera, Stream *stream) override; void freeFrameBuffers(Camera *camera, Stream *stream) override; - int allocateBuffers(Camera *camera, - const std::set &streams) override; - int freeBuffers(Camera *camera, - const std::set &streams) override; - int start(Camera *camera) override; void stop(Camera *camera) override; @@ -222,18 +217,6 @@ void PipelineHandlerUVC::freeFrameBuffers(Camera *camera, Stream *stream) data->video_->releaseBuffers(); } -int PipelineHandlerUVC::allocateBuffers(Camera *camera, - const std::set &streams) -{ - return 0; -} - -int PipelineHandlerUVC::freeBuffers(Camera *camera, - const std::set &streams) -{ - return 0; -} - int PipelineHandlerUVC::start(Camera *camera) { UVCCameraData *data = cameraData(camera); diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 4cfdb5ae79ce5779..1700ac967299b106 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -87,11 +87,6 @@ public: int importFrameBuffers(Camera *camera, Stream *stream) override; void freeFrameBuffers(Camera *camera, Stream *stream) override; - int allocateBuffers(Camera *camera, - const std::set &streams) override; - int freeBuffers(Camera *camera, - const std::set &streams) override; - int start(Camera *camera) override; void stop(Camera *camera) override; @@ -288,18 +283,6 @@ void PipelineHandlerVimc::freeFrameBuffers(Camera *camera, Stream *stream) data->video_->releaseBuffers(); } -int PipelineHandlerVimc::allocateBuffers(Camera *camera, - const std::set &streams) -{ - return 0; -} - -int PipelineHandlerVimc::freeBuffers(Camera *camera, - const std::set &streams) -{ - return 0; -} - int PipelineHandlerVimc::start(Camera *camera) { VimcCameraData *data = cameraData(camera); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 0348e3cfa68ed6ec..2fd65b468b9cd84f 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -356,35 +356,6 @@ const ControlInfoMap &PipelineHandler::controls(Camera *camera) * helper. */ -/** - * \fn PipelineHandler::allocateBuffers() - * \brief Allocate buffers for a stream - * \param[in] camera The camera the \a stream belongs to - * \param[in] streams The set of streams to allocate buffers for - * - * This method allocates buffers internally in the pipeline handler for each - * stream in the \a streams buffer set, 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 otherwise - */ - -/** - * \fn PipelineHandler::freeBuffers() - * \brief Free all buffers associated with a stream - * \param[in] camera The camera the \a stream belongs to - * \param[in] streams The set of streams to free buffers from - * - * After a capture session has been stopped all buffers associated with each - * stream shall be freed. - * - * The intended caller of this method is the Camera class. - * - * \return 0 on success or a negative error code otherwise - */ - /** * \fn PipelineHandler::start() * \brief Start capturing from a group of streams From patchwork Sun Jan 12 01:02:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 2614 Return-Path: Received: from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net [195.74.38.228]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7BE64606F3 for ; Sun, 12 Jan 2020 02:03:32 +0100 (CET) X-Halon-ID: 5779f690-34d7-11ea-b6d8-005056917f90 Authorized-sender: niklas@soderlund.pp.se Received: from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de [84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA id 5779f690-34d7-11ea-b6d8-005056917f90; Sun, 12 Jan 2020 02:03:28 +0100 (CET) From: =?utf-8?q?Niklas_S=C3=B6derlund?= To: libcamera-devel@lists.libcamera.org Date: Sun, 12 Jan 2020 02:02:12 +0100 Message-Id: <20200112010212.2609025-33-niklas.soderlund@ragnatech.se> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> References: <20200112010212.2609025-1-niklas.soderlund@ragnatech.se> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 32/32] libcamera: camera: Remove the prepared state X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Jan 2020 01:03:32 -0000 With the FrameBuffer rework completed there is no reason to keep the camera prepared state around as buffer allocations are now decoupled from the camera state. Remove the camera state simplifying the API. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart --- include/libcamera/camera.h | 4 - src/android/camera_device.cpp | 11 +-- src/cam/capture.cpp | 8 -- src/libcamera/camera.cpp | 100 +++++------------------- src/libcamera/framebuffer_allocator.cpp | 5 +- src/libcamera/pipeline_handler.cpp | 5 ++ src/qcam/main_window.cpp | 9 --- src/v4l2/v4l2_camera.cpp | 5 -- test/camera/buffer_import.cpp | 10 --- test/camera/capture.cpp | 10 --- test/camera/statemachine.cpp | 83 -------------------- 11 files changed, 27 insertions(+), 223 deletions(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 28655bec9ebc89ce..6597ade83288a170 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -91,9 +91,6 @@ public: std::unique_ptr generateConfiguration(const StreamRoles &roles); int configure(CameraConfiguration *config); - int allocateBuffers(); - int freeBuffers(); - Request *createRequest(uint64_t cookie = 0); int queueRequest(Request *request); @@ -105,7 +102,6 @@ private: CameraAvailable, CameraAcquired, CameraConfigured, - CameraPrepared, CameraRunning, }; diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 49321db07a2c93d5..a98fd744f5347432 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -77,8 +77,6 @@ int CameraDevice::open() void CameraDevice::close() { camera_->stop(); - - camera_->freeBuffers(); camera_->release(); running_ = false; @@ -690,16 +688,9 @@ void CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reque /* Start the camera if that's the first request we handle. */ if (!running_) { - int ret = camera_->allocateBuffers(); - if (ret) { - LOG(HAL, Error) << "Failed to allocate buffers"; - return; - } - - ret = camera_->start(); + int ret = camera_->start(); if (ret) { LOG(HAL, Error) << "Failed to start camera"; - camera_->freeBuffers(); return; } diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp index 738fa1c267eb6e36..7d970f991d3aaf1a 100644 --- a/src/cam/capture.cpp +++ b/src/cam/capture.cpp @@ -42,12 +42,6 @@ int Capture::run(EventLoop *loop, const OptionsParser::Options &options) return ret; } - ret = camera_->allocateBuffers(); - if (ret) { - std::cerr << "Failed to allocate buffers" << std::endl; - return ret; - } - camera_->requestCompleted.connect(this, &Capture::requestComplete); if (options.isSet(OptFile)) { @@ -67,8 +61,6 @@ int Capture::run(EventLoop *loop, const OptionsParser::Options &options) writer_ = nullptr; } - camera_->freeBuffers(); - delete allocator; return ret; diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index f3a7578d0834a9d6..79a5f994f9bbc8c1 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -275,15 +275,13 @@ std::size_t CameraConfiguration::size() const * \section camera_operation Operating the Camera * * An application needs to perform a sequence of operations on a camera before - * it is ready to process requests. The camera needs to be acquired, configured - * and resources allocated or imported to prepare the camera for capture. Once - * started the camera can process requests until it is stopped. When an - * application is done with a camera all resources allocated need to be freed - * and the camera released. + * it is ready to process requests. The camera needs to be acquired and + * configured to prepare the camera for capture. Once started the camera can + * process requests until it is stopped. When an application is done with a + * camera, the camera needs to be released. * * An application may start and stop a camera multiple times as long as it is - * not released. The camera may also be reconfigured provided that all - * resources allocated are freed prior to the reconfiguration. + * not released. The camera may also be reconfigured. * * \subsection Camera States * @@ -297,7 +295,6 @@ std::size_t CameraConfiguration::size() const * node [shape = doublecircle ]; Available; * node [shape = circle ]; Acquired; * node [shape = circle ]; Configured; - * node [shape = circle ]; Prepared; * node [shape = circle ]; Running; * * Available -> Available [label = "release()"]; @@ -307,14 +304,10 @@ std::size_t CameraConfiguration::size() const * Acquired -> Configured [label = "configure()"]; * * Configured -> Available [label = "release()"]; - * Configured -> Configured [label = "configure()"]; - * Configured -> Prepared [label = "allocateBuffers()"]; + * Configured -> Configured [label = "configure(), createRequest()"]; + * Configured -> Running [label = "start()"]; * - * Prepared -> Configured [label = "freeBuffers()"]; - * Prepared -> Prepared [label = "createRequest()"]; - * Prepared -> Running [label = "start()"]; - * - * Running -> Prepared [label = "stop()"]; + * Running -> Configured [label = "stop()"]; * Running -> Running [label = "createRequest(), queueRequest()"]; * } * \enddot @@ -330,19 +323,14 @@ std::size_t CameraConfiguration::size() const * Configured state. * * \subsubsection Configured - * The camera is configured and ready for the application to prepare it with - * resources. The camera may be reconfigured multiple times until resources - * are provided and the state progresses to Prepared. - * - * \subsubsection Prepared - * The camera has been configured and provided with resources and is ready to be - * started. The application may free the camera's resources to get back to the - * Configured state or start() it to progress to the Running state. + * The camera is configured and ready to be started. The application may + * release() the camera and to get back to the Available state or start() + * it to progress to the Running state. * * \subsubsection Running * The camera is running and ready to process requests queued by the * application. The camera remains in this state until it is stopped and moved - * to the Prepared state. + * to the Configured state. */ /** @@ -420,7 +408,6 @@ static const char *const camera_state_names[] = { "Available", "Acquired", "Configured", - "Prepared", "Running", }; @@ -465,8 +452,6 @@ bool Camera::stateIs(State state) const * * \todo Deal with pending requests if the camera is disconnected in a * running state. - * \todo Update comment about Running state when importing buffers as well as - * allocating them are supported. */ void Camera::disconnect() { @@ -474,11 +459,11 @@ void Camera::disconnect() /* * If the camera was running when the hardware was removed force the - * state to Prepared to allow applications to call freeBuffers() and - * release() before deleting the camera. + * state to Configured state to allow applications to free resources + * and call release() before deleting the camera. */ if (state_ == CameraRunning) - state_ = CameraPrepared; + state_ = CameraConfigured; disconnected_ = true; disconnected.emit(this); @@ -702,53 +687,6 @@ int Camera::configure(CameraConfiguration *config) return 0; } -/** - * \brief Allocate buffers for all configured streams - * - * This function affects the state of the camera, see \ref camera_operation. - * - * \return 0 on success or a negative error code otherwise - * \retval -ENODEV The camera has been disconnected from the system - * \retval -EACCES The camera is not in a state where buffers can be allocated - * \retval -EINVAL The configuration is not valid - */ -int Camera::allocateBuffers() -{ - if (disconnected_) - return -ENODEV; - - if (!stateIs(CameraConfigured)) - return -EACCES; - - if (activeStreams_.empty()) { - LOG(Camera, Error) - << "Can't allocate buffers without streams"; - return -EINVAL; - } - - state_ = CameraPrepared; - - return 0; -} - -/** - * \brief Release all buffers from allocated pools in each stream - * - * This function affects the state of the camera, see \ref camera_operation. - * - * \return 0 on success or a negative error code otherwise - * \retval -EACCES The camera is not in a state where buffers can be freed - */ -int Camera::freeBuffers() -{ - if (!stateIs(CameraPrepared)) - return -EACCES; - - state_ = CameraConfigured; - - return 0; -} - /** * \brief Create a request object for the camera * \param[in] cookie Opaque cookie for application use @@ -764,14 +702,14 @@ int Camera::freeBuffers() * The ownership of the returned request is passed to the caller, which is * responsible for either queueing the request or deleting it. * - * This function shall only be called when the camera is in the Prepared + * This function shall only be called when the camera is in the Configured * or Running state, see \ref camera_operation. * * \return A pointer to the newly created request, or nullptr on error */ Request *Camera::createRequest(uint64_t cookie) { - if (disconnected_ || !stateBetween(CameraPrepared, CameraRunning)) + if (disconnected_ || !stateBetween(CameraConfigured, CameraRunning)) return nullptr; return new Request(this, cookie); @@ -842,7 +780,7 @@ int Camera::start() if (disconnected_) return -ENODEV; - if (!stateIs(CameraPrepared)) + if (!stateIs(CameraConfigured)) return -EACCES; LOG(Camera, Debug) << "Starting capture"; @@ -885,7 +823,7 @@ int Camera::stop() LOG(Camera, Debug) << "Stopping capture"; - state_ = CameraPrepared; + state_ = CameraConfigured; pipe_->stop(this); diff --git a/src/libcamera/framebuffer_allocator.cpp b/src/libcamera/framebuffer_allocator.cpp index 57789b24e9615fa6..207a13bd841da2b8 100644 --- a/src/libcamera/framebuffer_allocator.cpp +++ b/src/libcamera/framebuffer_allocator.cpp @@ -116,8 +116,7 @@ FrameBufferAllocator::~FrameBufferAllocator() */ int FrameBufferAllocator::allocate(Stream *stream) { - if (camera_->state_ != Camera::CameraConfigured && - camera_->state_ != Camera::CameraPrepared) { + if (camera_->state_ != Camera::CameraConfigured) { LOG(Allocator, Error) << "Camera must be in the configured state to allocate buffers"; return -EACCES; @@ -163,7 +162,7 @@ int FrameBufferAllocator::allocate(Stream *stream) */ int FrameBufferAllocator::free(Stream *stream) { - if (camera_->state_ != Camera::CameraConfigured && camera_->state_ != Camera::CameraPrepared) { + if (camera_->state_ != Camera::CameraConfigured) { LOG(Allocator, Error) << "Camera must be in the configured state to free buffers"; return -EACCES; diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 2fd65b468b9cd84f..669097f609ab7168 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -301,6 +301,11 @@ const ControlInfoMap &PipelineHandler::controls(Camera *camera) * suitable to be added to a Request for the stream, and shall be mappable to * the CPU through their associated dmabufs with mmap(). * + * The method may only be called after the Camera has been configured and before + * it gets started, or after it gets stopped. It shall be called only for + * streams that are part of the active camera configuration, and at most once + * per stream until buffers for the stream are freed with freeFrameBuffers(). + * * exportFrameBuffers() shall also allocate all other resources required by * the pipeline handler for the stream to prepare for starting the Camera. This * responsibility is shared with importFrameBuffers(), and one and only one of diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 047bf15ea7c188f7..1d9c756f147a59f7 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -172,13 +172,6 @@ int MainWindow::startCapture() adjustSize(); - ret = camera_->allocateBuffers(); - if (ret) { - std::cerr << "Failed to allocate buffers" - << std::endl; - return ret; - } - ret = allocator_->allocate(stream); if (ret < 0) { std::cerr << "Failed to allocate capture buffers" << std::endl; @@ -244,7 +237,6 @@ error: } mappedBuffers_.clear(); - camera_->freeBuffers(); return ret; } @@ -264,7 +256,6 @@ void MainWindow::stopCapture() } mappedBuffers_.clear(); - camera_->freeBuffers(); isCapturing_ = false; config_.reset(); diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 10db15d6276d9bf3..44cb4e7c551be759 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -121,10 +121,6 @@ int V4L2Camera::configure(StreamConfiguration *streamConfigOut, int V4L2Camera::allocBuffers(unsigned int count) { - int ret = camera_->allocateBuffers(); - if (ret) - return ret == -EACCES ? -EBUSY : ret; - Stream *stream = *camera_->streams().begin(); return bufferAllocator_->allocate(stream); @@ -134,7 +130,6 @@ void V4L2Camera::freeBuffers() { Stream *stream = *camera_->streams().begin(); bufferAllocator_->free(stream); - camera_->freeBuffers(); } FileDescriptor V4L2Camera::getBufferFd(unsigned int index) diff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp index f506d1b221e568ad..e7048335e0317703 100644 --- a/test/camera/buffer_import.cpp +++ b/test/camera/buffer_import.cpp @@ -174,11 +174,6 @@ protected: return TestFail; } - if (camera_->allocateBuffers()) { - std::cout << "Failed to allocate buffers" << std::endl; - return TestFail; - } - Stream *stream = cfg.stream(); BufferSource source; @@ -244,11 +239,6 @@ protected: return TestFail; } - if (camera_->freeBuffers()) { - std::cout << "Failed to free buffers" << std::endl; - return TestFail; - } - return TestPass; } diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp index de879ee4eb1420a6..b304d59c1c2aa9e2 100644 --- a/test/camera/capture.cpp +++ b/test/camera/capture.cpp @@ -87,11 +87,6 @@ protected: return TestFail; } - if (camera_->allocateBuffers()) { - cout << "Failed to allocate buffers" << endl; - return TestFail; - } - Stream *stream = cfg.stream(); int ret = allocator_->allocate(stream); @@ -158,11 +153,6 @@ protected: return TestFail; } - if (camera_->freeBuffers()) { - cout << "Failed to free buffers" << endl; - return TestFail; - } - return TestPass; } diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp index f3a7ca7c32a5ec97..20541b3e4752dc81 100644 --- a/test/camera/statemachine.cpp +++ b/test/camera/statemachine.cpp @@ -29,12 +29,6 @@ protected: if (camera_->configure(defconf_.get()) != -EACCES) return TestFail; - if (camera_->allocateBuffers() != -EACCES) - return TestFail; - - if (camera_->freeBuffers() != -EACCES) - return TestFail; - if (camera_->createRequest()) return TestFail; @@ -65,12 +59,6 @@ protected: if (camera_->acquire() != -EBUSY) return TestFail; - if (camera_->allocateBuffers() != -EACCES) - return TestFail; - - if (camera_->freeBuffers() != -EACCES) - return TestFail; - if (camera_->createRequest()) return TestFail; @@ -103,57 +91,6 @@ protected: if (camera_->acquire() != -EBUSY) return TestFail; - if (camera_->freeBuffers() != -EACCES) - return TestFail; - - if (camera_->createRequest()) - return TestFail; - - Request request(camera_.get()); - if (camera_->queueRequest(&request) != -EACCES) - return TestFail; - - if (camera_->start() != -EACCES) - return TestFail; - - if (camera_->stop() != -EACCES) - return TestFail; - - /* Test operations which should pass. */ - if (camera_->configure(defconf_.get())) - return TestFail; - - /* Test valid state transitions, end in Prepared state. */ - if (camera_->release()) - return TestFail; - - if (camera_->acquire()) - return TestFail; - - if (camera_->configure(defconf_.get())) - return TestFail; - - if (camera_->allocateBuffers()) - return TestFail; - - return TestPass; - } - - int testPrepared() - { - /* Test operations which should fail. */ - if (camera_->acquire() != -EBUSY) - return TestFail; - - if (camera_->release() != -EBUSY) - return TestFail; - - if (camera_->configure(defconf_.get()) != -EACCES) - return TestFail; - - if (camera_->allocateBuffers() != -EACCES) - return TestFail; - Request request1(camera_.get()); if (camera_->queueRequest(&request1) != -EACCES) return TestFail; @@ -170,9 +107,6 @@ protected: delete request2; /* Test valid state transitions, end in Running state. */ - if (camera_->freeBuffers()) - return TestFail; - if (camera_->release()) return TestFail; @@ -182,9 +116,6 @@ protected: if (camera_->configure(defconf_.get())) return TestFail; - if (camera_->allocateBuffers()) - return TestFail; - /* Use internally allocated buffers. */ allocator_ = FrameBufferAllocator::create(camera_); Stream *stream = *camera_->streams().begin(); @@ -209,12 +140,6 @@ protected: if (camera_->configure(defconf_.get()) != -EACCES) return TestFail; - if (camera_->allocateBuffers() != -EACCES) - return TestFail; - - if (camera_->freeBuffers() != -EACCES) - return TestFail; - if (camera_->start() != -EACCES) return TestFail; @@ -236,9 +161,6 @@ protected: delete allocator_; - if (camera_->freeBuffers()) - return TestFail; - if (camera_->release()) return TestFail; @@ -276,11 +198,6 @@ protected: return TestFail; } - if (testPrepared() != TestPass) { - cout << "State machine in Prepared state failed" << endl; - return TestFail; - } - if (testRuning() != TestPass) { cout << "State machine in Running state failed" << endl; return TestFail;