summaryrefslogtreecommitdiff
path: root/test/media_device/media_device_link_test.cpp
blob: fe7542bbb6106933942db3f17e6ddaaff7a0452d (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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * media_device_link_test.cpp - Tests link handling on VIMC media device
 */

#include <iostream>

#include "media_device_test.h"

using namespace libcamera;
using namespace std;

/*
 * This link test requires a vimc device in order to exercise the
 * MediaObject link handling API on a graph with a predetermined topology.
 *
 * vimc is a Media Controller kernel driver that creates virtual devices.
 * From a userspace point of view they appear as normal media controller
 * devices, but are not backed by any particular piece of hardware. They can
 * thus be used for testing purpose without depending on a particular hardware
 * platform.
 *
 * If no vimc device is found (most likely because the vimc driver is not
 * loaded) the test is skipped.
 */

class MediaDeviceLinkTest : public MediaDeviceTest
{
	int init()
	{
		int ret = MediaDeviceTest::init();
		if (ret)
			return ret;

		if (!media_->acquire()) {
			cerr << "Unable to acquire media device "
			     << media_->deviceNode() << endl;
			return TestFail;
		}

		return TestPass;
	}

	int run()
	{
		/*
		 * First of all disable all links in the media graph to
		 * ensure we start from a known state.
		 */
		if (media_->disableLinks()) {
			cerr << "Failed to disable all links in the media graph";
			return TestFail;
		}

		/*
		 * Test if link can be consistently retrieved through the
		 * different methods the media device offers.
		 */
		string linkName("'Debayer A':[1] -> 'Scaler':[0]'");
		MediaLink *link = media_->link("Debayer A", 1, "Scaler", 0);
		if (!link) {
			cerr << "Unable to find link: " << linkName
			     << " using lookup by name" << endl;
			return TestFail;
		}

		MediaEntity *source = media_->getEntityByName("Debayer A");
		if (!source) {
			cerr << "Unable to find entity: 'Debayer A'" << endl;
			return TestFail;
		}

		MediaEntity *sink = media_->getEntityByName("Scaler");
		if (!sink) {
			cerr << "Unable to find entity: 'Scaler'" << endl;
			return TestFail;
		}

		MediaLink *link2 = media_->link(source, 1, sink, 0);
		if (!link2) {
			cerr << "Unable to find link: " << linkName
			     << " using lookup by entity" << endl;
			return TestFail;
		}

		if (link != link2) {
			cerr << "Link lookup by name and by entity don't match"
			     << endl;
			return TestFail;
		}

		link2 = media_->link(source->getPadByIndex(1),
				   sink->getPadByIndex(0));
		if (!link2) {
			cerr << "Unable to find link: " << linkName
			     << " using lookup by pad" << endl;
			return TestFail;
		}

		if (link != link2) {
			cerr << "Link lookup by name and by pad don't match"
			     << endl;
			return TestFail;
		}

		/* After reset the link shall not be enabled. */
		if (link->flags() & MEDIA_LNK_FL_ENABLED) {
			cerr << "Link " << linkName
			     << " should not be enabled after a device reset"
			     << endl;
			return TestFail;
		}

		/* Enable the link and test if enabling was successful. */
		if (link->setEnabled(true)) {
			cerr << "Failed to enable link: " << linkName
			     << endl;
			return TestFail;
		}

		if (!(link->flags() & MEDIA_LNK_FL_ENABLED)) {
			cerr << "Link " << linkName
			     << " was enabled but it is reported as disabled"
			     << endl;
			return TestFail;
		}

		/* Disable the link and test if disabling was successful. */
		if (link->setEnabled(false)) {
			cerr << "Failed to disable link: " << linkName
			     << endl;
			return TestFail;
		}

		if (link->flags() & MEDIA_LNK_FL_ENABLED) {
			cerr << "Link " << linkName
			     << " was disabled but it is reported as enabled"
			     << endl;
			return TestFail;
		}

		/* Try to get a non existing link. */
		linkName = "'Sensor A':[1] -> 'Scaler':[0]";
		link = media_->link("Sensor A", 1, "Scaler", 0);
		if (link) {
			cerr << "Link lookup for " << linkName
			     << " succeeded but link does not exist"
			     << endl;
			return TestFail;
		}

		/* Now get an immutable link and try to disable it. */
		linkName = "'Sensor A':[0] -> 'Raw Capture 0':[0]";
		link = media_->link("Sensor A", 0, "Raw Capture 0", 0);
		if (!link) {
			cerr << "Unable to find link: " << linkName
			     << " using lookup by name" << endl;
			return TestFail;
		}

		if (!(link->flags() & MEDIA_LNK_FL_IMMUTABLE)) {
			cerr << "Link " << linkName
			     << " should be 'IMMUTABLE'" << endl;
			return TestFail;
		}

		/* Disabling an immutable link shall fail. */
		if (!link->setEnabled(false)) {
			cerr << "Disabling immutable link " << linkName
			     << " succeeded but should have failed" << endl;
			return TestFail;
		}

		/*
		 * Enable an disabled link, and verify it is disabled again
		 * after disabling all links in the media graph.
		 */
		linkName = "'Debayer B':[1] -> 'Scaler':[0]'";
		link = media_->link("Debayer B", 1, "Scaler", 0);
		if (!link) {
			cerr << "Unable to find link: " << linkName
			     << " using lookup by name" << endl;
			return TestFail;
		}

		if (link->setEnabled(true)) {
			cerr << "Failed to enable link: " << linkName
			     << endl;
			return TestFail;
		}

		if (!(link->flags() & MEDIA_LNK_FL_ENABLED)) {
			cerr << "Link " << linkName
			     << " was enabled but it is reported as disabled"
			     << endl;
			return TestFail;
		}

		if (media_->disableLinks()) {
			cerr << "Failed to disable all links in the media graph";
			return TestFail;
		}

		if (link->flags() & MEDIA_LNK_FL_ENABLED) {
			cerr << "All links in the media graph have been disabled"
			     << " but link " << linkName
			     << " is still reported as enabled"  << endl;
			return TestFail;
		}

		return 0;
	}

	void cleanup()
	{
		media_->release();
	}
};

TEST_REGISTER(MediaDeviceLinkTest);
opt">() const { return data_.empty(); } /** * \brief Retrieve a list of all supported image formats * \return List of pixel formats or media bus codes */ std::vector<unsigned int> ImageFormats::formats() const { std::vector<unsigned int> formats; formats.reserve(data_.size()); /* \todo: Should this be cached instead of computed each time? */ for (auto const &it : data_) formats.push_back(it.first); return formats; } /** * \brief Retrieve all sizes for a specific format * \param[in] format The pixel format or mbus code * * Retrieve all size ranges for a specific format. For V4L2Device \a format is a * pixel format while for a V4L2Subdevice \a format is a media bus code. * * \return The list of image sizes supported for \a format, or an empty list if * the format is not supported */ const std::vector<SizeRange> &ImageFormats::sizes(unsigned int format) const { static const std::vector<SizeRange> empty; auto const &it = data_.find(format); if (it == data_.end()) return empty; return it->second; } /** * \brief Retrieve the map that associates formats to image sizes * \return The map that associates formats to image sizes */ const std::map<unsigned int, std::vector<SizeRange>> &ImageFormats::data() const { return data_; } /** * \class PixelFormatPlaneInfo * \brief Information about a single plane of a pixel format * * \var PixelFormatPlaneInfo::bytesPerGroup * \brief The number of bytes that a pixel group consumes * * \sa PixelFormatInfo::pixelsPerGroup * * \var PixelFormatPlaneInfo::verticalSubSampling * \brief Vertical subsampling multiplier * * This value is the ratio between the number of rows of pixels in the frame * to the number of rows of pixels in the plane. */ /** * \class PixelFormatInfo * \brief Information about pixel formats * * The PixelFormatInfo class groups together information describing a pixel * format. It facilitates handling of pixel formats by providing data commonly * used in pipeline handlers. * * \var PixelFormatInfo::name * \brief The format name as a human-readable string, used as the test * representation of the PixelFormat * * \var PixelFormatInfo::format * \brief The PixelFormat described by this instance * * \var PixelFormatInfo::v4l2Format * \brief The V4L2 pixel format corresponding to the PixelFormat * * \var PixelFormatInfo::bitsPerPixel * \brief The average number of bits per pixel * * The number per pixel averages the total number of bits for all colour * components over the whole image, excluding any padding bits or padding * pixels. * * For formats that store pixels with bit padding within words, only the * effective bits are taken into account. For instance, 12-bit Bayer data * stored in two bytes per pixel report 12, not 16, in this field. * * Formats that don't have a fixed number of bits per pixel, such as compressed * formats, report 0 in this field. * * \var PixelFormatInfo::colourEncoding * \brief The colour encoding type * * \var PixelFormatInfo::packed * \brief Tell if multiple pixels are packed in the same bytes * * Packed formats are defined as storing data from multiple pixels in the same * bytes. For instance, 12-bit Bayer data with two pixels stored in three bytes * is packed, while the same data stored with 4 bits of padding in two bytes * per pixel is not packed. * * \var PixelFormatInfo::pixelsPerGroup * \brief The number of pixels in a pixel group * * A pixel group is defined as the minimum number of pixels (including padding) * necessary in a row when the image has only one column of effective pixels. * pixelsPerGroup refers to this value. PixelFormatPlaneInfo::bytesPerGroup, * then, refers to the number of bytes that a pixel group consumes. This * definition of a pixel group allows simple calculation of stride, as * ceil(width / pixelsPerGroup) * bytesPerGroup. These values are determined * only in terms of a row. The ceiling accounts for padding. * * A pixel group has a second constraint, such that the pixel group * (bytesPerGroup and pixelsPerGroup) is the smallest repeatable unit. * What this means is that, for example, in the IPU3 formats, if there is only * one column of effective pixels, it looks like it could be fit in 5 bytes * with 3 padding pixels (for a total of 4 pixels over 5 bytes). However, this * unit is not repeatable, as at the 7th group in the same row, the pattern * is broken. Therefore, the pixel group for IPU3 formats must be 25 pixels * over 32 bytes. * * For example, for something simple like BGR888, it is self-explanatory: * the pixel group size is 1, and the bytes necessary is 3, and there is * only one plane with no (= 1) vertical subsampling. For YUYV, the * CbCr pair is shared between two pixels, so even if you have only one * pixel, you would still need a padded second Y sample, therefore the pixel * group size is 2, and bytes necessary is 4. YUYV also has no vertical * subsampling. NV12 has a pixel group size of 2 pixels, due to the CbCr plane. * The bytes per group then, for both planes, is 2. The first plane has no * vertical subsampling, but the second plane is subsampled by a factor of 2. * * The IPU3 raw Bayer formats are single-planar, and have a pixel group size of * 25, consuming 32 bytes, due to the packing pattern being repeated in memory * every 32 bytes. The IPU3 hardware, however, has an additional constraint on * the DMA burst size, requiring lines to be multiple of 64 bytes. This isn't an * intrinsic property of the formats and is thus not reflected here. It is * instead enforced by the corresponding pipeline handler. * * \var PixelFormatInfo::planes * \brief Information about pixels for each plane * * \sa PixelFormatPlaneInfo */ /** * \enum PixelFormatInfo::ColourEncoding * \brief The colour encoding type * * \var PixelFormatInfo::ColourEncodingRGB * \brief RGB colour encoding * * \var PixelFormatInfo::ColourEncodingYUV * \brief YUV colour encoding * * \var PixelFormatInfo::ColourEncodingRAW * \brief RAW colour encoding */ namespace { const PixelFormatInfo pixelFormatInfoInvalid{}; const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{ /* RGB formats. */ { formats::BGR888, { .name = "BGR888", .format = formats::BGR888, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGB24), .bitsPerPixel = 24, .colourEncoding = PixelFormatInfo::ColourEncodingRGB, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::RGB888, { .name = "RGB888", .format = formats::RGB888, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGR24), .bitsPerPixel = 24, .colourEncoding = PixelFormatInfo::ColourEncodingRGB, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::ABGR8888, { .name = "ABGR8888", .format = formats::ABGR8888, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32), .bitsPerPixel = 32, .colourEncoding = PixelFormatInfo::ColourEncodingRGB, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::ARGB8888, { .name = "ARGB8888", .format = formats::ARGB8888, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32), .bitsPerPixel = 32, .colourEncoding = PixelFormatInfo::ColourEncodingRGB, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::BGRA8888, { .name = "BGRA8888", .format = formats::BGRA8888, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32), .bitsPerPixel = 32, .colourEncoding = PixelFormatInfo::ColourEncodingRGB, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::RGBA8888, { .name = "RGBA8888", .format = formats::RGBA8888, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32), .bitsPerPixel = 32, .colourEncoding = PixelFormatInfo::ColourEncodingRGB, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, /* YUV packed formats. */ { formats::YUYV, { .name = "YUYV", .format = formats::YUYV, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YUYV), .bitsPerPixel = 16, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::YVYU, { .name = "YVYU", .format = formats::YVYU, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_YVYU), .bitsPerPixel = 16, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::UYVY, { .name = "UYVY", .format = formats::UYVY, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_UYVY), .bitsPerPixel = 16, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, { formats::VYUY, { .name = "VYUY", .format = formats::VYUY, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_VYUY), .bitsPerPixel = 16, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }}, } }, /* YUV planar formats. */ { formats::NV12, { .name = "NV12", .format = formats::NV12, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV12), .bitsPerPixel = 12, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 2, 1 }, { 2, 2 }, { 0, 0 } }}, } }, { formats::NV21, { .name = "NV21", .format = formats::NV21, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV21), .bitsPerPixel = 12, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 2, 1 }, { 2, 2 }, { 0, 0 } }}, } }, { formats::NV16, { .name = "NV16", .format = formats::NV16, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV16), .bitsPerPixel = 16, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 2, 1 }, { 2, 1 }, { 0, 0 } }}, } }, { formats::NV61, { .name = "NV61", .format = formats::NV61, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV61), .bitsPerPixel = 16, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 2, .planes = {{ { 2, 1 }, { 2, 1 }, { 0, 0 } }}, } }, { formats::NV24, { .name = "NV24", .format = formats::NV24, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV24), .bitsPerPixel = 24, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 1, 1 }, { 2, 1 }, { 0, 0 } }}, } }, { formats::NV42, { .name = "NV42", .format = formats::NV42, .v4l2Format = V4L2PixelFormat(V4L2_PIX_FMT_NV42), .bitsPerPixel = 24, .colourEncoding = PixelFormatInfo::ColourEncodingYUV, .packed = false, .pixelsPerGroup = 1, .planes = {{ { 1, 1 }, { 2, 1 }, { 0, 0 } }}, } },