summaryrefslogtreecommitdiff
path: root/test/v4l2_videodevice/v4l2_m2mdevice.cpp
blob: d132b1db2432b25921d082f2499621f34de6c68c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

 * unique_fd.h - File descriptor wrapper that owns a file descriptor.
 */

#pragma once

#include <utility>

#include <libcamera/base/class.h>
#include <libcamera/base/compiler.h>

namespace libcamera {

class UniqueFD final
{
public:
	UniqueFD()
		: fd_(-1)
	{
	}

	explicit UniqueFD(int fd)
		: fd_(fd)
	{
	}

	UniqueFD(UniqueFD &&other)
		: fd_(other.release())
	{
	}

	~UniqueFD()
	{
		reset();
	}

	UniqueFD &operator=(UniqueFD &&other)
	{
		reset(other.release());
		return *this;
	}

	__nodiscard int release()
	{
		int fd = fd_;
		fd_ = -1;
		return fd;
	}

	void reset(int fd = -1);

	void swap(UniqueFD &other)
	{
		std::swap(fd_, other.fd_);
	}

	int get() const { return fd_; }
	bool isValid() const { return fd_ >= 0; }

private:
	LIBCAMERA_DISABLE_COPY(UniqueFD)

	/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * libcamera V4L2 M2M video device tests
 */

#include <iostream>

#include <libcamera/buffer.h>
#include <libcamera/camera_manager.h>
#include <libcamera/event_dispatcher.h>
#include <libcamera/timer.h>

#include "device_enumerator.h"
#include "media_device.h"
#include "v4l2_videodevice.h"

#include "test.h"

using namespace std;
using namespace libcamera;

class V4L2M2MDeviceTest : public Test
{
public:
	V4L2M2MDeviceTest()
		: vim2m_(nullptr), outputFrames_(0), captureFrames_(0)
	{
	}

	void outputBufferComplete(Buffer *buffer)
	{
		cout << "Received output buffer " << buffer->index() << endl;

		outputFrames_++;

		/* Requeue the buffer for further use. */
		vim2m_->output()->queueBuffer(buffer);
	}

	void receiveCaptureBuffer(Buffer *buffer)
	{
		cout << "Received capture buffer " << buffer->index() << endl;

		captureFrames_++;

		/* Requeue the buffer for further use. */
		vim2m_->capture()->queueBuffer(buffer);
	}

protected:
	int init()
	{
		enumerator_ = DeviceEnumerator::create();
		if (!enumerator_) {
			cerr << "Failed to create device enumerator" << endl;
			return TestFail;
		}

		if (enumerator_->enumerate()) {
			cerr << "Failed to enumerate media devices" << endl;
			return TestFail;
		}

		DeviceMatch dm("vim2m");
		dm.add("vim2m-source");
		dm.add("vim2m-sink");

		media_ = enumerator_->search(dm);
		if (!media_) {
			cerr << "No vim2m device found" << endl;
			return TestSkip;
		}

		return TestPass;
	}

	int run()
	{
		constexpr unsigned int bufferCount = 4;

		EventDispatcher *dispatcher = CameraManager::instance()->eventDispatcher();
		int ret;

		MediaEntity *entity = media_->getEntityByName("vim2m-source");
		vim2m_ = new V4L2M2MDevice(entity->deviceNode());
		if (vim2m_->open()) {
			cerr << "Failed to open VIM2M device" << endl;
			return TestFail;
		}

		V4L2VideoDevice *capture = vim2m_->capture();
		V4L2VideoDevice *output = vim2m_->output();

		V4L2DeviceFormat format = {};
		if (capture->getFormat(&format)) {
			cerr << "Failed to get capture format" << endl;
			return TestFail;
		}

		format.size.width = 640;
		format.size.height = 480;

		if (capture->setFormat(&format)) {
			cerr << "Failed to set capture format" << endl;
			return TestFail;
		}

		if (output->setFormat(&format)) {
			cerr << "Failed to set output format" << endl;
			return TestFail;
		}

		capturePool_.createBuffers(bufferCount);
		outputPool_.createBuffers(bufferCount);

		ret = capture->exportBuffers(&capturePool_);
		if (ret) {
			cerr << "Failed to export Capture Buffers" << endl;
			return TestFail;
		}

		ret = output->exportBuffers(&outputPool_);
		if (ret) {
			cerr << "Failed to export Output Buffers" << endl;
			return TestFail;
		}

		capture->bufferReady.connect(this, &V4L2M2MDeviceTest::receiveCaptureBuffer);
		output->bufferReady.connect(this, &V4L2M2MDeviceTest::outputBufferComplete);

		std::vector<std::unique_ptr<Buffer>> captureBuffers;
		captureBuffers = capture->queueAllBuffers();
		if (captureBuffers.empty()) {
			cerr << "Failed to queue all Capture Buffers" << endl;
			return TestFail;
		}

		/* We can't "queueAllBuffers()" on an output device, so we do it manually */
		std::vector<std::unique_ptr<Buffer>> 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;
				return TestFail;
			}
		}

		ret = capture->streamOn();
		if (ret) {
			cerr << "Failed to streamOn capture" << endl;
			return TestFail;
		}

		ret = output->streamOn();
		if (ret) {
			cerr << "Failed to streamOn output" << endl;
			return TestFail;
		}

		Timer timeout;
		timeout.start(5000);
		while (timeout.isRunning()) {
			dispatcher->processEvents();
			if (captureFrames_ > 30)
				break;
		}

		cerr << "Output " << outputFrames_ << " frames" << std::endl;
		cerr << "Captured " << captureFrames_ << " frames" << std::endl;

		if (captureFrames_ < 30) {
			cerr << "Failed to capture 30 frames within timeout." << std::endl;
			return TestFail;
		}

		ret = capture->streamOff();
		if (ret) {
			cerr << "Failed to StreamOff the capture device." << std::endl;
			return TestFail;
		}

		ret = output->streamOff();
		if (ret) {
			cerr << "Failed to StreamOff the output device." << std::endl;
			return TestFail;
		}

		return TestPass;
	}

	void cleanup()
	{
		delete vim2m_;
	};

private:
	std::unique_ptr<DeviceEnumerator> enumerator_;
	std::shared_ptr<MediaDevice> media_;
	V4L2M2MDevice *vim2m_;

	BufferPool capturePool_;
	BufferPool outputPool_;

	unsigned int outputFrames_;
	unsigned int captureFrames_;
};

TEST_REGISTER(V4L2M2MDeviceTest);