From patchwork Sun Jun 30 18:10:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1534 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 34E0460BC0 for ; Sun, 30 Jun 2019 20:09:38 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id CD8EA200007; Sun, 30 Jun 2019 18:09:37 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:42 +0200 Message-Id: <20190630181049.9548-2-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 1/8] libcamera: v4l2_videodevice: Re-group operations X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:38 -0000 Group together operations to enumerate formats and operations to handle memory handling, alternating public and private operations but respecting the ordering within each group. Cosmetic change to prepare for a re-work of the memory handling operations. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund --- src/libcamera/include/v4l2_videodevice.h | 6 +- src/libcamera/v4l2_videodevice.cpp | 170 +++++++++++------------ 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index 734b34f1dc53..b92df882568f 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -160,13 +160,13 @@ private: int getFormatSingleplane(V4L2DeviceFormat *format); int setFormatSingleplane(V4L2DeviceFormat *format); + std::vector enumPixelformats(); + std::vector enumSizes(unsigned int pixelFormat); + int requestBuffers(unsigned int count); int createPlane(Buffer *buffer, unsigned int plane, unsigned int length); - std::vector enumPixelformats(); - std::vector enumSizes(unsigned int pixelFormat); - Buffer *dequeueBuffer(); void bufferAvailable(EventNotifier *notifier); diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 12af3bd0639b..2d1e87a76c6f 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -627,6 +627,91 @@ ImageFormats V4L2VideoDevice::formats() return formats; } +std::vector V4L2VideoDevice::enumPixelformats() +{ + std::vector formats; + int ret; + + for (unsigned int index = 0; ; index++) { + struct v4l2_fmtdesc pixelformatEnum = {}; + pixelformatEnum.index = index; + pixelformatEnum.type = bufferType_; + + ret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum); + if (ret) + break; + + formats.push_back(pixelformatEnum.pixelformat); + } + + if (ret && ret != -EINVAL) { + LOG(V4L2, Error) + << "Unable to enumerate pixel formats: " + << strerror(-ret); + return {}; + } + + return formats; +} + +std::vector V4L2VideoDevice::enumSizes(unsigned int pixelFormat) +{ + std::vector sizes; + int ret; + + for (unsigned int index = 0;; index++) { + struct v4l2_frmsizeenum frameSize = {}; + frameSize.index = index; + frameSize.pixel_format = pixelFormat; + + ret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize); + if (ret) + break; + + if (index != 0 && + frameSize.type != V4L2_FRMSIZE_TYPE_DISCRETE) { + LOG(V4L2, Error) + << "Non-zero index for non discrete type"; + return {}; + } + + switch (frameSize.type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + sizes.emplace_back(frameSize.discrete.width, + frameSize.discrete.height); + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + sizes.emplace_back(frameSize.stepwise.min_width, + frameSize.stepwise.min_height, + frameSize.stepwise.max_width, + frameSize.stepwise.max_height); + break; + case V4L2_FRMSIZE_TYPE_STEPWISE: + sizes.emplace_back(frameSize.stepwise.min_width, + frameSize.stepwise.min_height, + frameSize.stepwise.max_width, + frameSize.stepwise.max_height, + frameSize.stepwise.step_width, + frameSize.stepwise.step_height); + break; + default: + LOG(V4L2, Error) + << "Unknown VIDIOC_ENUM_FRAMESIZES type " + << frameSize.type; + return {}; + } + } + + if (ret && ret != -EINVAL) { + LOG(V4L2, Error) + << "Unable to enumerate frame sizes: " + << strerror(-ret); + return {}; + } + + return sizes; +} + int V4L2VideoDevice::requestBuffers(unsigned int count) { struct v4l2_requestbuffers rb = {}; @@ -754,91 +839,6 @@ int V4L2VideoDevice::createPlane(Buffer *buffer, unsigned int planeIndex, return 0; } -std::vector V4L2VideoDevice::enumPixelformats() -{ - std::vector formats; - int ret; - - for (unsigned int index = 0; ; index++) { - struct v4l2_fmtdesc pixelformatEnum = {}; - pixelformatEnum.index = index; - pixelformatEnum.type = bufferType_; - - ret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum); - if (ret) - break; - - formats.push_back(pixelformatEnum.pixelformat); - } - - if (ret && ret != -EINVAL) { - LOG(V4L2, Error) - << "Unable to enumerate pixel formats: " - << strerror(-ret); - return {}; - } - - return formats; -} - -std::vector V4L2VideoDevice::enumSizes(unsigned int pixelFormat) -{ - std::vector sizes; - int ret; - - for (unsigned int index = 0;; index++) { - struct v4l2_frmsizeenum frameSize = {}; - frameSize.index = index; - frameSize.pixel_format = pixelFormat; - - ret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize); - if (ret) - break; - - if (index != 0 && - frameSize.type != V4L2_FRMSIZE_TYPE_DISCRETE) { - LOG(V4L2, Error) - << "Non-zero index for non discrete type"; - return {}; - } - - switch (frameSize.type) { - case V4L2_FRMSIZE_TYPE_DISCRETE: - sizes.emplace_back(frameSize.discrete.width, - frameSize.discrete.height); - break; - case V4L2_FRMSIZE_TYPE_CONTINUOUS: - sizes.emplace_back(frameSize.stepwise.min_width, - frameSize.stepwise.min_height, - frameSize.stepwise.max_width, - frameSize.stepwise.max_height); - break; - case V4L2_FRMSIZE_TYPE_STEPWISE: - sizes.emplace_back(frameSize.stepwise.min_width, - frameSize.stepwise.min_height, - frameSize.stepwise.max_width, - frameSize.stepwise.max_height, - frameSize.stepwise.step_width, - frameSize.stepwise.step_height); - break; - default: - LOG(V4L2, Error) - << "Unknown VIDIOC_ENUM_FRAMESIZES type " - << frameSize.type; - return {}; - } - } - - if (ret && ret != -EINVAL) { - LOG(V4L2, Error) - << "Unable to enumerate frame sizes: " - << strerror(-ret); - return {}; - } - - return sizes; -} - /** * \brief Import the externally allocated \a pool of buffers * \param[in] pool BufferPool of buffers to import From patchwork Sun Jun 30 18:10:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1535 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B6E8561EB4 for ; Sun, 30 Jun 2019 20:09:38 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id 5B8F7200004; Sun, 30 Jun 2019 18:09:38 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:43 +0200 Message-Id: <20190630181049.9548-3-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 2/8] libcamera: stream: Provide accessors to buffers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:38 -0000 All interactions with the Stream's buffers currently go through the BufferPool. In order to shorten accessing the buffers array, and restrict access to the Stream's internal buffer pool, provide operations to access the buffers, create and destriy them. It is still possible to access the pool for pipeline handlers to populate it by exporting buffers from a video device to the pool. Signed-off-by: Jacopo Mondi --- include/libcamera/stream.h | 12 ++++++++++ src/cam/capture.cpp | 4 ++-- src/libcamera/camera.cpp | 6 ++--- src/libcamera/pipeline/ipu3/ipu3.cpp | 4 ++-- src/libcamera/stream.cpp | 35 +++++++++++++++++++++++----- src/qcam/main_window.cpp | 4 +--- test/camera/capture.cpp | 3 +-- test/camera/statemachine.cpp | 3 +-- 8 files changed, 51 insertions(+), 20 deletions(-) diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index 5b4fea324ce4..fa7d6ba4987c 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -66,12 +66,24 @@ class Stream { public: Stream(); + /* + * FIXME: + * If I could find a way to export buffers in pipeline handlers + * without accessing the pool with + * video_->exportBuffers(&stream->bufferPool()); + * we could remove access to the internal pool completely. + */ BufferPool &bufferPool() { return bufferPool_; } + std::vector &buffers() { return bufferPool_.buffers(); } + unsigned int bufferCount() const { return bufferPool_.count(); } const StreamConfiguration &configuration() const { return configuration_; } protected: friend class Camera; + void createBuffers(unsigned int count); + void destroyBuffers(); + BufferPool bufferPool_; StreamConfiguration configuration_; }; diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp index 6b842d73390d..1bcc9c7e9cf4 100644 --- a/src/cam/capture.cpp +++ b/src/cam/capture.cpp @@ -76,7 +76,7 @@ int Capture::capture(EventLoop *loop) unsigned int nbuffers = UINT_MAX; for (StreamConfiguration &cfg : *config_) { Stream *stream = cfg.stream(); - nbuffers = std::min(nbuffers, stream->bufferPool().count()); + nbuffers = std::min(nbuffers, stream->bufferCount()); } /* @@ -95,7 +95,7 @@ int Capture::capture(EventLoop *loop) std::map map; for (StreamConfiguration &cfg : *config_) { Stream *stream = cfg.stream(); - map[stream] = &stream->bufferPool().buffers()[i]; + map[stream] = &stream->buffers()[i]; } ret = request->setBuffers(map); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 617ea99cdf71..023ae53e5f9d 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -671,7 +671,7 @@ int Camera::configure(CameraConfiguration *config) * Allocate buffer objects in the pool. * Memory will be allocated and assigned later. */ - stream->bufferPool().createBuffers(cfg.bufferCount); + stream->createBuffers(cfg.bufferCount); } state_ = CameraConfigured; @@ -728,14 +728,14 @@ int Camera::freeBuffers() return -EACCES; for (Stream *stream : activeStreams_) { - if (!stream->bufferPool().count()) + if (!stream->bufferCount()) continue; /* * All mappings must be destroyed before buffers can be freed * by the V4L2 device that has allocated them. */ - stream->bufferPool().destroyBuffers(); + stream->destroyBuffers(); } state_ = CameraConfigured; diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index e4efb9722f76..2de0892138a8 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -634,7 +634,7 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera, * of buffers as the active ones. */ if (!outStream->active_) { - bufferCount = vfStream->bufferPool().count(); + bufferCount = vfStream->bufferCount(); outStream->device_->pool->createBuffers(bufferCount); ret = imgu->exportBuffers(outStream->device_, outStream->device_->pool); @@ -643,7 +643,7 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera, } if (!vfStream->active_) { - bufferCount = outStream->bufferPool().count(); + bufferCount = outStream->bufferCount(); vfStream->device_->pool->createBuffers(bufferCount); ret = imgu->exportBuffers(vfStream->device_, vfStream->device_->pool); diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index d8e87c62281c..c6701e5f9921 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -408,14 +408,12 @@ Stream::Stream() } /** - * \fn Stream::bufferPool() - * \brief Retrieve the buffer pool for the stream + * \fn Stream::buffers() + * \brief Retrieve the stream's buffers * - * The buffer pool handles the buffers used to capture frames at the output of - * the stream. It is initially created empty and shall be populated with - * buffers before being used. + * \todo * - * \return A reference to the buffer pool + * \return The list of stream's buffers */ /** @@ -424,6 +422,31 @@ Stream::Stream() * \return The active configuration of the stream */ +/** + * \brief Create buffers for the stream + * + * \todo + */ +void Stream::createBuffers(unsigned int count) +{ + bufferPool_.destroyBuffers(); + + if (count == 0) + return; + + bufferPool_.createBuffers(count); +} + +/** + * \brief Destroy buffers in the stream + * + * \todo + */ +void Stream::destroyBuffers() +{ + createBuffers(0); +} + /** * \var Stream::bufferPool_ * \brief The pool of buffers associated with the stream diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 16b123132dd9..a0703b322c16 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -122,10 +122,8 @@ int MainWindow::startCapture() return ret; } - BufferPool &pool = stream->bufferPool(); std::vector requests; - - for (Buffer &buffer : pool.buffers()) { + for (Buffer &buffer : stream->buffers()) { Request *request = camera_->createRequest(); if (!request) { std::cerr << "Can't create request" << std::endl; diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp index 7ce247cc482d..a0385ec2c74d 100644 --- a/test/camera/capture.cpp +++ b/test/camera/capture.cpp @@ -76,9 +76,8 @@ protected: } Stream *stream = cfg.stream(); - BufferPool &pool = stream->bufferPool(); std::vector requests; - for (Buffer &buffer : pool.buffers()) { + for (Buffer &buffer : stream->buffers()) { Request *request = camera_->createRequest(); if (!request) { cout << "Failed to create request" << endl; diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp index 84d2a6fab5f0..c23455b5bb21 100644 --- a/test/camera/statemachine.cpp +++ b/test/camera/statemachine.cpp @@ -211,8 +211,7 @@ protected: return TestFail; Stream *stream = *camera_->streams().begin(); - BufferPool &pool = stream->bufferPool(); - Buffer &buffer = pool.buffers().front(); + Buffer &buffer = stream->buffers().front(); std::map map = { { stream, &buffer } }; if (request->setBuffers(map)) return TestFail; From patchwork Sun Jun 30 18:10:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1536 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 441E761F28 for ; Sun, 30 Jun 2019 20:09:39 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id DA275200004; Sun, 30 Jun 2019 18:09:38 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:44 +0200 Message-Id: <20190630181049.9548-4-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 3/8] libcamera: stream: Add Stream memory type X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:40 -0000 Define the memory type a Stream uses and allow application to set it through the associated StreamConfiguration. A Stream can use either internal or external memory allocation methods, depending on where the data produced by the stream is actually saved. Use the memory type flag in pipeline handlers (todo: change all pipeline handlers) during buffer setup operations, to decide if the Stream's internal memory has to be exported to applications, or the Stream should prepare to use buffers whose memory is allocated elsewhere. To support the last use case, a translation mechanism between the external buffers provided by applications and internal ones used by pipeline handlers to feed the video device will be implemented on top of this change. Signed-off-by: Jacopo Mondi --- include/libcamera/stream.h | 8 ++++++++ src/libcamera/camera.cpp | 1 + src/libcamera/pipeline/uvcvideo.cpp | 8 +++++++- src/libcamera/pipeline/vimc.cpp | 8 +++++++- src/libcamera/stream.cpp | 14 ++++++++++++-- 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index fa7d6ba4987c..796f1aff2602 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -34,6 +34,11 @@ private: std::map> formats_; }; +enum MemoryType { + InternalMemory, + ExternalMemory, +}; + struct StreamConfiguration { StreamConfiguration(); StreamConfiguration(const StreamFormats &formats); @@ -41,6 +46,7 @@ struct StreamConfiguration { unsigned int pixelFormat; Size size; + MemoryType memoryType; unsigned int bufferCount; Stream *stream() const { return stream_; } @@ -77,6 +83,7 @@ public: std::vector &buffers() { return bufferPool_.buffers(); } unsigned int bufferCount() const { return bufferPool_.count(); } const StreamConfiguration &configuration() const { return configuration_; } + MemoryType memoryType() const { return memoryType_; } protected: friend class Camera; @@ -86,6 +93,7 @@ protected: BufferPool bufferPool_; StreamConfiguration configuration_; + MemoryType memoryType_; }; } /* namespace libcamera */ diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 023ae53e5f9d..662406b970dc 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -671,6 +671,7 @@ int Camera::configure(CameraConfiguration *config) * Allocate buffer objects in the pool. * Memory will be allocated and assigned later. */ + stream->memoryType_ = cfg.memoryType; stream->createBuffers(cfg.bufferCount); } diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index 2e22523d7cb1..5112356c25ac 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -190,10 +190,16 @@ int PipelineHandlerUVC::allocateBuffers(Camera *camera, UVCCameraData *data = cameraData(camera); Stream *stream = *streams.begin(); const StreamConfiguration &cfg = stream->configuration(); + int ret; LOG(UVC, Debug) << "Requesting " << cfg.bufferCount << " buffers"; - return data->video_->exportBuffers(&stream->bufferPool()); + if (stream->memoryType() == InternalMemory) + ret = data->video_->exportBuffers(&stream->bufferPool()); + else + ret = data->video_->importBuffers(&stream->bufferPool()); + + return ret; } int PipelineHandlerUVC::freeBuffers(Camera *camera, diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 6833213650dc..21a37dba1fc6 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -189,10 +189,16 @@ int PipelineHandlerVimc::allocateBuffers(Camera *camera, VimcCameraData *data = cameraData(camera); Stream *stream = *streams.begin(); const StreamConfiguration &cfg = stream->configuration(); + int ret; LOG(VIMC, Debug) << "Requesting " << cfg.bufferCount << " buffers"; - return data->video_->exportBuffers(&stream->bufferPool()); + if (stream->memoryType() == InternalMemory) + ret = data->video_->exportBuffers(&stream->bufferPool()); + else + ret = data->video_->importBuffers(&stream->bufferPool()); + + return ret; } int PipelineHandlerVimc::freeBuffers(Camera *camera, diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index c6701e5f9921..b6292427d3a2 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -263,6 +263,11 @@ SizeRange StreamFormats::range(unsigned int pixelformat) const return range; } +/** + * \enum MemoryType + * \todo + */ + /** * \struct StreamConfiguration * \brief Configuration parameters for a stream @@ -276,7 +281,7 @@ SizeRange StreamFormats::range(unsigned int pixelformat) const * handlers provied StreamFormats. */ StreamConfiguration::StreamConfiguration() - : stream_(nullptr) + : memoryType(InternalMemory), stream_(nullptr) { } @@ -284,7 +289,7 @@ StreamConfiguration::StreamConfiguration() * \brief Construct a configuration with stream formats */ StreamConfiguration::StreamConfiguration(const StreamFormats &formats) - : stream_(nullptr), formats_(formats) + : memoryType(InternalMemory), stream_(nullptr), formats_(formats) { } @@ -301,6 +306,11 @@ StreamConfiguration::StreamConfiguration(const StreamFormats &formats) * format described in V4L2 using the V4L2_PIX_FMT_* definitions. */ +/** + * \var memoryType + * \todo + */ + /** * \var StreamConfiguration::bufferCount * \brief Requested number of buffers to allocate for the stream From patchwork Sun Jun 30 18:10:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1537 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C24E961F4A for ; Sun, 30 Jun 2019 20:09:39 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id 64CD5200003; Sun, 30 Jun 2019 18:09:39 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:45 +0200 Message-Id: <20190630181049.9548-5-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 4/8] libcamera: Rename PipelineHandler::allocateBuffers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:40 -0000 Now that the pipeline handlers can inspect the Stream's memory type flag the allocateBuffers() operation name does not match the actual operation purpose, which is to setup buffers to export memory to application or either prepare to use externally allocated buffers. Todo: rename the camera operation as well? Signed-off-by: Jacopo Mondi --- src/libcamera/camera.cpp | 2 +- src/libcamera/include/pipeline_handler.h | 4 ++-- src/libcamera/pipeline/ipu3/ipu3.cpp | 8 ++++---- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 6 +++--- src/libcamera/pipeline/uvcvideo.cpp | 8 ++++---- src/libcamera/pipeline/vimc.cpp | 8 ++++---- src/libcamera/pipeline_handler.cpp | 6 ++++-- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 662406b970dc..a71b44b82ea0 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -704,7 +704,7 @@ int Camera::allocateBuffers() return -EINVAL; } - int ret = pipe_->allocateBuffers(this, activeStreams_); + int ret = pipe_->setupBuffers(this, activeStreams_); if (ret) { LOG(Camera, Error) << "Failed to allocate buffers"; return ret; diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index a4cbc593a179..df479d92883a 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -64,8 +64,8 @@ public: const StreamRoles &roles) = 0; virtual int configure(Camera *camera, CameraConfiguration *config) = 0; - virtual int allocateBuffers(Camera *camera, - const std::set &streams) = 0; + virtual int setupBuffers(Camera *camera, + const std::set &streams) = 0; virtual int freeBuffers(Camera *camera, const std::set &streams) = 0; diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 2de0892138a8..2975b59a0953 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -207,8 +207,8 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; - int allocateBuffers(Camera *camera, - const std::set &streams) override; + int setupBuffers(Camera *camera, + const std::set &streams) override; int freeBuffers(Camera *camera, const std::set &streams) override; @@ -588,8 +588,8 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c) * 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::setupBuffers(Camera *camera, + const std::set &streams) { IPU3CameraData *data = cameraData(camera); IPU3Stream *outStream = &data->outStream_; diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 4a5898d25f91..716428e33d6c 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -80,7 +80,7 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; - int allocateBuffers(Camera *camera, + int setupBuffers(Camera *camera, const std::set &streams) override; int freeBuffers(Camera *camera, const std::set &streams) override; @@ -315,8 +315,8 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c) return 0; } -int PipelineHandlerRkISP1::allocateBuffers(Camera *camera, - const std::set &streams) +int PipelineHandlerRkISP1::setupBuffers(Camera *camera, + const std::set &streams) { Stream *stream = *streams.begin(); return video_->exportBuffers(&stream->bufferPool()); diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index 5112356c25ac..e66a55c83c66 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -58,8 +58,8 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; - int allocateBuffers(Camera *camera, - const std::set &streams) override; + int setupBuffers(Camera *camera, + const std::set &streams) override; int freeBuffers(Camera *camera, const std::set &streams) override; @@ -184,8 +184,8 @@ int PipelineHandlerUVC::configure(Camera *camera, CameraConfiguration *config) return 0; } -int PipelineHandlerUVC::allocateBuffers(Camera *camera, - const std::set &streams) +int PipelineHandlerUVC::setupBuffers(Camera *camera, + const std::set &streams) { UVCCameraData *data = cameraData(camera); Stream *stream = *streams.begin(); diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 21a37dba1fc6..3d48305f4b76 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -62,8 +62,8 @@ public: const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; - int allocateBuffers(Camera *camera, - const std::set &streams) override; + int setupBuffers(Camera *camera, + const std::set &streams) override; int freeBuffers(Camera *camera, const std::set &streams) override; @@ -183,8 +183,8 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config) return 0; } -int PipelineHandlerVimc::allocateBuffers(Camera *camera, - const std::set &streams) +int PipelineHandlerVimc::setupBuffers(Camera *camera, + const std::set &streams) { VimcCameraData *data = cameraData(camera); Stream *stream = *streams.begin(); diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index c91ef2f7d336..fbebe4a04acd 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -261,11 +261,13 @@ void PipelineHandler::unlock() */ /** - * \fn PipelineHandler::allocateBuffers() - * \brief Allocate buffers for a stream + * \fn PipelineHandler::setupBuffers() + * \brief Setup buffer for a stream * \param[in] camera The camera the \a stream belongs to * \param[in] streams The set of streams to allocate buffers for * + * \todo Change this to describe both allocation and importing + * * 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. From patchwork Sun Jun 30 18:10:46 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1538 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4852861F28 for ; Sun, 30 Jun 2019 20:09:40 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id E0F46200003; Sun, 30 Jun 2019 18:09:39 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:46 +0200 Message-Id: <20190630181049.9548-6-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 5/8] libcamera: request: Rename the Stream to Buffer map X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:40 -0000 As we're about to add support for mapping application buffers to streams' ones, rename the existing bufferMap_ to just buffers_, which also matches the associated accessor operation name. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund --- include/libcamera/request.h | 4 ++-- src/libcamera/camera.cpp | 2 +- src/libcamera/request.cpp | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 58de6f00a554..7adb753b2706 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -32,7 +32,7 @@ public: Request(const Request &) = delete; Request &operator=(const Request &) = delete; - const std::map &buffers() const { return bufferMap_; } + const std::map &buffers() const { return buffers_; } int setBuffers(const std::map &streamMap); Buffer *findBuffer(Stream *stream) const; @@ -50,7 +50,7 @@ private: bool completeBuffer(Buffer *buffer); Camera *camera_; - std::map bufferMap_; + std::map buffers_; std::unordered_set pending_; Status status_; diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index a71b44b82ea0..4b66d6216950 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -883,7 +883,7 @@ int Camera::stop() */ void Camera::requestComplete(Request *request) { - std::map buffers(std::move(request->bufferMap_)); + std::map buffers(std::move(request->buffers_)); requestCompleted.emit(request, buffers); delete request; } diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index fa3ee46da440..def7f2f21dc7 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -70,20 +70,20 @@ Request::Request(Camera *camera) */ int Request::setBuffers(const std::map &streamMap) { - if (!bufferMap_.empty()) { + if (!buffers_.empty()) { LOG(Request, Error) << "Buffers already set"; return -EBUSY; } - bufferMap_ = streamMap; + buffers_ = streamMap; return 0; } /** - * \var Request::bufferMap_ + * \var Request::buffers_ * \brief Mapping of streams to buffers for this request * - * The bufferMap_ tracks the buffers associated with each stream. If a stream is + * The buffers_ tracks the buffers associated with each stream. If a stream is * not utilised in this request there will be no buffer for that stream in the * map. */ @@ -96,8 +96,8 @@ int Request::setBuffers(const std::map &streamMap) */ Buffer *Request::findBuffer(Stream *stream) const { - auto it = bufferMap_.find(stream); - if (it == bufferMap_.end()) + auto it = buffers_.find(stream); + if (it == buffers_.end()) return nullptr; return it->second; @@ -134,12 +134,12 @@ Buffer *Request::findBuffer(Stream *stream) const */ int Request::prepare() { - if (bufferMap_.empty()) { + if (buffers_.empty()) { LOG(Request, Error) << "Invalid request due to missing buffers"; return -EINVAL; } - for (auto const &pair : bufferMap_) { + for (auto const &pair : buffers_) { Buffer *buffer = pair.second; buffer->setRequest(this); pending_.insert(buffer); From patchwork Sun Jun 30 18:10:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1539 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C362D61F28 for ; Sun, 30 Jun 2019 20:09:40 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id 67D77200004; Sun, 30 Jun 2019 18:09:40 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:47 +0200 Message-Id: <20190630181049.9548-7-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 6/8] libcamera: stream: Add operation to map buffers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:40 -0000 Add and operation to map external buffers provided by applications in a Request to the Stream's internal buffers used by the pipeline handlers to interact with the video device. For streams using internal memory allocation, the two buffers are the same as applications effectively use Buffers from the Stream's pool where the video device memory has been exported to. Signed-off-by: Jacopo Mondi --- include/libcamera/buffer.h | 1 + include/libcamera/stream.h | 5 +++ src/libcamera/stream.cpp | 66 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h index 260a62e9e77e..d5d3dc90a096 100644 --- a/include/libcamera/buffer.h +++ b/include/libcamera/buffer.h @@ -59,6 +59,7 @@ private: friend class BufferPool; friend class PipelineHandler; friend class Request; + friend class Stream; friend class V4L2VideoDevice; void cancel(); diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index 796f1aff2602..4c034c113ddb 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -85,6 +85,8 @@ public: const StreamConfiguration &configuration() const { return configuration_; } MemoryType memoryType() const { return memoryType_; } + Buffer *mapBuffer(Buffer *requestBuffer); + protected: friend class Camera; @@ -94,6 +96,9 @@ protected: BufferPool bufferPool_; StreamConfiguration configuration_; MemoryType memoryType_; + + std::vector mappableBuffers_; + std::map bufferMaps_; }; } /* namespace libcamera */ diff --git a/src/libcamera/stream.cpp b/src/libcamera/stream.cpp index b6292427d3a2..f36336857ad6 100644 --- a/src/libcamera/stream.cpp +++ b/src/libcamera/stream.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include "log.h" /** @@ -445,6 +447,26 @@ void Stream::createBuffers(unsigned int count) return; bufferPool_.createBuffers(count); + + /* Streams with internal memory usage do not need buffer mapping. */ + if (memoryType_ == InternalMemory) + return; + + /* + * Prepare for buffer mapping by queuing all buffers from the internal + * pool. Each external buffer presented by application will be mapped + * on an internal one. + */ + mappableBuffers_.clear(); + for (Buffer &buffer : bufferPool_.buffers()) { + /* Reserve all planes to support mapping multiplanar buffers. */ + buffer.planes().clear(); + /* \todo: I would use VIDEO_MAX_PLANES but that's V4L2 stuff.. */ + for (unsigned int i = 0; i < 3; ++i) + buffer.planes().emplace_back(); + + mappableBuffers_.push_back(&buffer); + } } /** @@ -457,6 +479,50 @@ void Stream::destroyBuffers() createBuffers(0); } +/** + * \brief Map the buffer an application has associated with a Request to an + * internl one + * + * \todo Rewrite documentation + * If the Stream uses external memory, we need to map the externally + * provided buffer to an internal one, trying to keep a best effort + * association based on the Buffer's last usage time. + * External and internal buffers are associated by using the dmabuf + * fds as key. + */ +Buffer *Stream::mapBuffer(Buffer *requestBuffer) +{ + /* + * \todo Multiplane APIs have one fd per plane, the key should be + * hashed using all the planes fds. + */ + unsigned int key = requestBuffer->planes()[0].dmabuf(); + + /* If the buffer has already been mapped, just return it. */ + auto mapped = bufferMaps_.find(key); + if (mapped != bufferMaps_.end()) + return mapped->second; + + /* + * Remove the last recently used buffer from the circular list and + * use it for mapping. + */ + auto mappable = mappableBuffers_.begin(); + Buffer *buffer = *mappable; + mappableBuffers_.erase(mappable); + mappableBuffers_.push_back(buffer); + + /* \todo: Support multiplanar external buffers. */ + buffer->planes()[0].setDmabuf(key, 0); + + /* Pipeline handlers use request_ at buffer completion time. */ + buffer->request_ = requestBuffer->request(); + + bufferMaps_[key] = buffer; + + return buffer; +} + /** * \var Stream::bufferPool_ * \brief The pool of buffers associated with the stream From patchwork Sun Jun 30 18:10:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1540 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 46FC561F63 for ; Sun, 30 Jun 2019 20:09:41 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id DEDB2200003; Sun, 30 Jun 2019 18:09:40 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:48 +0200 Message-Id: <20190630181049.9548-8-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 7/8] libcamera: request: Support buffer mapping X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:42 -0000 Use the Stream class buffer mapping operation to save the association in the request at Request::findBuffer() time and reverse it with a new operation Request::unmapBuffer() used by the pipeline handler base class at buffer completion time, to return to the applications the Buffer they originally provided with the Request without involving the pipeline handlers. Signed-off-by: Jacopo Mondi --- include/libcamera/request.h | 4 +++- src/libcamera/pipeline_handler.cpp | 6 ++++-- src/libcamera/request.cpp | 27 ++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 7adb753b2706..c22b6be282b2 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -34,7 +34,8 @@ public: const std::map &buffers() const { return buffers_; } int setBuffers(const std::map &streamMap); - Buffer *findBuffer(Stream *stream) const; + Buffer *findBuffer(Stream *stream); + Buffer *unmapBuffer(Buffer *streamBuffer); Status status() const { return status_; } @@ -52,6 +53,7 @@ private: Camera *camera_; std::map buffers_; std::unordered_set pending_; + std::map bufferMap_; Status status_; }; diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index fbebe4a04acd..1dcfefaaf6eb 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -383,8 +383,10 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request) bool PipelineHandler::completeBuffer(Camera *camera, Request *request, Buffer *buffer) { - camera->bufferCompleted.emit(request, buffer); - return request->completeBuffer(buffer); + Buffer *requestBuffer = request->unmapBuffer(buffer); + + camera->bufferCompleted.emit(request, requestBuffer); + return request->completeBuffer(requestBuffer); } /** diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index def7f2f21dc7..bfe6fc99a93b 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -94,12 +94,37 @@ int Request::setBuffers(const std::map &streamMap) * \return The buffer associated with the stream, or nullptr if the stream is * not part of this request */ -Buffer *Request::findBuffer(Stream *stream) const +Buffer *Request::findBuffer(Stream *stream) { auto it = buffers_.find(stream); if (it == buffers_.end()) return nullptr; + /* + * Streams with internal memory mode do not need to perform any mapping + * between the application provided buffers (part of the request) + * and the one actually used by the Stream. + * + * Streams using externally allocated buffers need to create a mapping + * between the application provided buffers and the one used by pipeline + * handlers. + */ + Buffer *requestBuffer = it->second; + Buffer *mappedBuffer = stream->memoryType() == InternalMemory ? + it->second : stream->mapBuffer(it->second); + bufferMap_[mappedBuffer] = requestBuffer; + + return mappedBuffer; +} + +/** + * \brief Retrieve the application buffer associated with \a streamBuffer + */ +Buffer *Request::unmapBuffer(Buffer *streamBuffer) +{ + auto it = bufferMap_.find(streamBuffer); + ASSERT(it != bufferMap_.end()); + return it->second; } From patchwork Sun Jun 30 18:10:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1541 Return-Path: Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C956461F4E for ; Sun, 30 Jun 2019 20:09:41 +0200 (CEST) Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay12.mail.gandi.net (Postfix) with ESMTPSA id 65193200004; Sun, 30 Jun 2019 18:09:41 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 30 Jun 2019 20:10:49 +0200 Message-Id: <20190630181049.9548-9-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190630181049.9548-1-jacopo@jmondi.org> References: <20190630181049.9548-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC 8/8] test: v4l2_videodevice: Add buffer import test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Jun 2019 18:09:42 -0000 Test buffer importing by streaming the camera to a video output device performing zero-copy memory sharing using dmabuf file descriptors. Signed-off-by: Jacopo Mondi --- test/v4l2_videodevice/buffer_import.cpp | 234 ++++++++++++++++++++++++ test/v4l2_videodevice/meson.build | 1 + 2 files changed, 235 insertions(+) create mode 100644 test/v4l2_videodevice/buffer_import.cpp diff --git a/test/v4l2_videodevice/buffer_import.cpp b/test/v4l2_videodevice/buffer_import.cpp new file mode 100644 index 000000000000..0a294b055af5 --- /dev/null +++ b/test/v4l2_videodevice/buffer_import.cpp @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * libcamera V4L2 API tests + * + * Test importing buffers exported from an output device into a camera + */ + +#include + +#include +#include +#include +#include +#include + +#include "v4l2_videodevice_test.h" + +using namespace libcamera; +using namespace std; + +class BufferImportTest : public V4L2VideoDeviceTest +{ +public: + BufferImportTest() + : V4L2VideoDeviceTest("vivid", "vivid-000-vid-out") + { + } + +protected: + void cameraBufferComplete(Request *request, Buffer *buffer) + { + if (buffer->status() != Buffer::BufferSuccess) + return; + + capture_->queueBuffer(buffer); + } + + void requestComplete(Request *request, const std::map &buffers) + { + if (request->status() != Request::RequestComplete) + return; + + /* Reuse the buffers for a new request. */ + request = camera_->createRequest(); + request->setBuffers(buffers); + camera_->queueRequest(request); + } + + int init() + { + constexpr unsigned int bufferCount = 4; + + /* Get a camera where to capture frames from. */ + cm_ = CameraManager::instance(); + + if (cm_->start()) { + cout << "Failed to start camera manager" << endl; + return TestFail; + } + + camera_ = cm_->get("Integrated Camera: Integrated C"); + if (!camera_) { + cout << "Can not find VIMC camera" << endl; + return TestSkip; + } + + if (camera_->acquire()) { + cout << "Failed to acquire the camera" << endl; + return TestFail; + } + + /* + * Initialize the output device and export buffers in a pool. + * The 'output' device is actually called capture_ by the base + * class. + */ + int ret = V4L2VideoDeviceTest::init(); + if (ret) { + cerr << "Failed to initialize output device" << endl; + return ret; + } + + /* + * Set a known format on the output devices, then apply it + * to the camera. + */ + V4L2DeviceFormat format = {}; + if (capture_->getFormat(&format)) { + cleanup(); + return TestFail; + } + + format.size.width = 640; + format.size.height = 480; + format.fourcc = V4L2_PIX_FMT_YUYV; + format.planesCount = 1; + format.planes[0].size = 640 * 480 * 2; + format.planes[0].bpl = 640 * 2; + if (capture_->setFormat(&format)) { + cleanup(); + return TestFail; + } + + cout << "Output format: " << format.toString(); + + config_ = camera_->generateConfiguration({ StreamRole::VideoRecording }); + if (!config_ || config_->size() != 1) { + cout << "Failed to generate default configuration" << endl; + cleanup(); + return TestFail; + } + + /* + * Configure the Stream to work with externally allocated + * buffers by setting the memoryType to ExternalMemory. + */ + StreamConfiguration &cfg = config_->at(0); + cfg.size = format.size; + cfg.pixelFormat = format.fourcc; + cfg.memoryType = ExternalMemory; + + if (camera_->configure(config_.get())) { + cout << "Failed to set modified configuration" << endl; + cleanup(); + return TestFail; + } + cout << "Capture format: " << format.toString(); + + /* + * Export the output buffers to a pool and then import + * them before setting up buffers in the Camera. + */ + pool_.createBuffers(bufferCount); + ret = capture_->exportBuffers(&pool_); + if (ret) { + std::cout << "Failed to export buffers" << std::endl; + cleanup(); + return TestFail; + } + + if (camera_->allocateBuffers()) { + cout << "Failed to allocate buffers" << endl; + return TestFail; + } + + return TestPass; + } + + int run() + { + std::vector requests; + StreamConfiguration &cfg = config_->at(0); + Stream *stream = cfg.stream(); + /* Create one request for each output video buffer. */ + for (Buffer &buffer : pool_.buffers()) { + Request *request = camera_->createRequest(); + if (!request) { + cout << "Failed to create request" << endl; + return TestFail; + } + + std::map map = { { stream, &buffer } }; + if (request->setBuffers(map)) { + cout << "Failed to associating buffer with request" << endl; + return TestFail; + } + + requests.push_back(request); + } + + /* Connect the buffer ready signals of camera and output */ + camera_->bufferCompleted.connect(this, + &BufferImportTest::cameraBufferComplete); + + /* Connect the request ready signal to re-queue requests. */ + camera_->requestCompleted.connect(this, + &BufferImportTest::requestComplete); + + capture_->streamOn(); + if (camera_->start()) { + cout << "Failed to start camera" << endl; + return TestFail; + } + + for (Request *request : requests) { + if (camera_->queueRequest(request)) { + cout << "Failed to queue request" << endl; + camera_->stop(); + capture_->streamOff(); + cleanup(); + return TestFail; + } + } + + EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher(); + + Timer timer; + timer.start(2000); + while (timer.isRunning()) + dispatcher->processEvents(); + + if (camera_->stop()) { + cout << "Failed to stop camera" << endl; + return TestFail; + } + + capture_->streamOff(); + + return TestPass; + } + + void cleanup() + { + camera_->freeBuffers(); + + if (camera_) { + camera_->release(); + camera_.reset(); + } + + cm_->stop(); + + V4L2VideoDeviceTest::cleanup(); + } + +private: + CameraManager *cm_; + std::shared_ptr camera_; + std::unique_ptr config_; +}; + +TEST_REGISTER(BufferImportTest); diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build index 76be5e142bb6..15169abe48d3 100644 --- a/test/v4l2_videodevice/meson.build +++ b/test/v4l2_videodevice/meson.build @@ -7,6 +7,7 @@ v4l2_videodevice_tests = [ [ 'stream_on_off', 'stream_on_off.cpp' ], [ 'capture_async', 'capture_async.cpp' ], [ 'buffer_sharing', 'buffer_sharing.cpp' ], + [ 'buffer_import', 'buffer_import.cpp' ], ] foreach t : v4l2_videodevice_tests