Message ID | 20190704225334.26170-10-jacopo@jmondi.org |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi Jacopo, On 04/07/2019 23:53, Jacopo Mondi wrote: > Test buffer importing by streaming the camera to a video output device > performing zero-copy memory sharing using dmabuf file descriptors. > > Not-yet-Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Following through this to see how the new usages will be: > > --- > Not suitable for merge yes. More a test utility for development at the > moment. To be morhped into a camera test from a video device one. > --- > 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 <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. > + */ Perhaps for the test/ demo it's worth just creating the output device manually then? (I know this is a dummy hack/wip, so not exactly a necessity here,) > + 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; Ok - so with ExternalMemory, no pool is imported into the Stream, It's just (arbitrary) buffers being added at each request? > + > + 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_); I would have imagined we could do something like stream_->importBuffers(pool_); ? (which would also then set the ExternalMemory flag, because that becomes implicit) > + if (ret) { > + std::cout << "Failed to export buffers" << std::endl; > + cleanup(); > + return TestFail; > + } > + > + if (camera_->allocateBuffers()) { And I guess this doesn't allocateBuffers any more? Ahh yes, I see an earlier patch has suggested renaming this already... I agree - setupBuffers() could be more appropriate at the moment... > + 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; s/associating/associate/ > + 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. */ We requeue buffers in a new request, the request is not re-queued. (we don't/can't re-use requests currently). > + 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 >
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