[libcamera-devel] [RFC 8/8] test: v4l2_videodevice: Add buffer import test
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Tue Jul 2 01:57:41 CEST 2019
Hi Jacopo,
Thank you for the patch.
On Sun, Jun 30, 2019 at 08:10:49PM +0200, Jacopo Mondi wrote:
> 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 <jacopo at jmondi.org>
> ---
> 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
I'll do a more in-depth review, but I think you could already move this
test to test/camera as it's not a v4l2_videodevice test (unlike the
buffer sharing test that tests buffer sharing between V4L2 video
devices).
> 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 <iostream>
> +
> +#include <libcamera/buffer.h>
> +#include <libcamera/camera.h>
> +#include <libcamera/camera_manager.h>
> +#include <libcamera/event_dispatcher.h>
> +#include <libcamera/timer.h>
> +
> +#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<Stream *, Buffer *> &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<Request *> 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<Stream *, Buffer *> 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> camera_;
> + std::unique_ptr<CameraConfiguration> 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
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list