Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlinuxmediadevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "qlinuxmediadevice.h"
7
8#include <QtCore/QLoggingCategory>
9#include <QtCore/QSize>
10#include <QtCore/QRect>
11
12#include <sys/ioctl.h>
13#include <fcntl.h>
14
15#include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
16extern "C" {
17#include <mediactl/mediactl.h>
18#include <mediactl/v4l2subdev.h>
19}
20
22
23Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
24
26{
27 switch (code) {
28 case MEDIA_BUS_FMT_FIXED: return "FIXED";
29 case MEDIA_BUS_FMT_RGB444_1X12: return "RGB444_1X12";
30// case MEDIA_BUS_FMT_RGB444_2X8_PADHI_B: return "RGB444_2X8_PADHI_B";
31// case MEDIA_BUS_FMT_RGB444_2X8_PADHI_L: return "RGB444_2X8_PADHI_L";
32// case MEDIA_BUS_FMT_RGB555_2X8_PADHI_B: return "RGB555_2X8_PADHI_B";
33// case MEDIA_BUS_FMT_RGB555_2X8_PADHI_L: return "RGB555_2X8_PADHI_L";
34 case MEDIA_BUS_FMT_RGB565_1X16: return "RGB565_1X16";
35 case MEDIA_BUS_FMT_BGR565_2X8_BE: return "BGR565_2X8_BE";
36 case MEDIA_BUS_FMT_BGR565_2X8_LE: return "BGR565_2X8_LE";
37 case MEDIA_BUS_FMT_RGB565_2X8_BE: return "RGB565_2X8_BE";
38 case MEDIA_BUS_FMT_RGB565_2X8_LE: return "RGB565_2X8_LE";
39 case MEDIA_BUS_FMT_RGB666_1X18: return "RGB666_1X18";
40 case MEDIA_BUS_FMT_RBG888_1X24: return "RBG888_1X24";
41// case MEDIA_BUS_FMT_RGB666_1X24_CPADH: return "RGB666_1X24_CPADH";
42 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: return "RGB666_1X7X3_SPWG";
43 case MEDIA_BUS_FMT_BGR888_1X24: return "BGR888_1X24";
44 case MEDIA_BUS_FMT_GBR888_1X24: return "GBR888_1X24";
45 case MEDIA_BUS_FMT_RGB888_1X24: return "RGB888_1X24";
46 case MEDIA_BUS_FMT_RGB888_2X12_BE: return "RGB888_2X12_BE";
47 case MEDIA_BUS_FMT_RGB888_2X12_LE: return "RGB888_2X12_LE";
48 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: return "RGB888_1X7X4_SPWG";
49// case MEDIA_BUS_FMT_RGB888_1X7X4_JEID: return "RGB888_1X7X4_JEID";
50 case MEDIA_BUS_FMT_ARGB8888_1X32: return "ARGB8888_1X32";
51 case MEDIA_BUS_FMT_RGB888_1X32_PADHI: return "RGB888_1X32_PADHI";
52// case MEDIA_BUS_FMT_RGB101010_1X30: return "RGB101010_1X30";
53// case MEDIA_BUS_FMT_RGB121212_1X36: return "RGB121212_1X36";
54// case MEDIA_BUS_FMT_RGB161616_1X48: return "RGB161616_1X48";
55 case MEDIA_BUS_FMT_Y8_1X8: return "Y8_1X8";
56 case MEDIA_BUS_FMT_UV8_1X8: return "UV8_1X8";
57 case MEDIA_BUS_FMT_UYVY8_1_5X8: return "UYVY8_1_5X8";
58 case MEDIA_BUS_FMT_VYUY8_1_5X8: return "VYUY8_1_5X8";
59 case MEDIA_BUS_FMT_YUYV8_1_5X8: return "YUYV8_1_5X8";
60 case MEDIA_BUS_FMT_YVYU8_1_5X8: return "YVYU8_1_5X8";
61 case MEDIA_BUS_FMT_UYVY8_2X8: return "UYVY8_2X8";
62 case MEDIA_BUS_FMT_VYUY8_2X8: return "VYUY8_2X8";
63 case MEDIA_BUS_FMT_YUYV8_2X8: return "YUYV8_2X8";
64 case MEDIA_BUS_FMT_YVYU8_2X8: return "YVYU8_2X8";
65 case MEDIA_BUS_FMT_Y10_1X10: return "Y10_1X10";
66 case MEDIA_BUS_FMT_UYVY10_2X10: return "UYVY10_2X10";
67 case MEDIA_BUS_FMT_VYUY10_2X10: return "VYUY10_2X10";
68 case MEDIA_BUS_FMT_YUYV10_2X10: return "YUYV10_2X10";
69 case MEDIA_BUS_FMT_YVYU10_2X10: return "YVYU10_2X10";
70 case MEDIA_BUS_FMT_Y12_1X12: return "Y12_1X12";
71 case MEDIA_BUS_FMT_UYVY12_2X12: return "UYVY12_2X12";
72 case MEDIA_BUS_FMT_VYUY12_2X12: return "VYUY12_2X12";
73 case MEDIA_BUS_FMT_YUYV12_2X12: return "YUYV12_2X12";
74 case MEDIA_BUS_FMT_YVYU12_2X12: return "YVYU12_2X12";
75 case MEDIA_BUS_FMT_UYVY8_1X16: return "UYVY8_1X16";
76 case MEDIA_BUS_FMT_VYUY8_1X16: return "VYUY8_1X16";
77 case MEDIA_BUS_FMT_YUYV8_1X16: return "YUYV8_1X16";
78 case MEDIA_BUS_FMT_YVYU8_1X16: return "YVYU8_1X16";
79 case MEDIA_BUS_FMT_YDYUYDYV8_1X16: return "YDYUYDYV8_1X16";
80 case MEDIA_BUS_FMT_UYVY10_1X20: return "UYVY10_1X20";
81 case MEDIA_BUS_FMT_VYUY10_1X20: return "VYUY10_1X20";
82 case MEDIA_BUS_FMT_YUYV10_1X20: return "YUYV10_1X20";
83 case MEDIA_BUS_FMT_YVYU10_1X20: return "YVYU10_1X20";
84 case MEDIA_BUS_FMT_VUY8_1X24: return "VUY8_1X24";
85 case MEDIA_BUS_FMT_YUV8_1X24: return "YUV8_1X24";
86// case MEDIA_BUS_FMT_UYYVYY8_0_5X24: return "UYYVYY8_0_5X24";
87 case MEDIA_BUS_FMT_UYVY12_1X24: return "UYVY12_1X24";
88 case MEDIA_BUS_FMT_VYUY12_1X24: return "VYUY12_1X24";
89 case MEDIA_BUS_FMT_YUYV12_1X24: return "YUYV12_1X24";
90 case MEDIA_BUS_FMT_YVYU12_1X24: return "YVYU12_1X24";
91 case MEDIA_BUS_FMT_YUV10_1X30: return "YUV10_1X30";
92// case MEDIA_BUS_FMT_UYYVYY10_0_5X30: return "UYYVYY10_0_5X30";
93 case MEDIA_BUS_FMT_AYUV8_1X32: return "AYUV8_1X32";
94// case MEDIA_BUS_FMT_UYYVYY12_0_5X36: return "UYYVYY12_0_5X36";
95// case MEDIA_BUS_FMT_YUV12_1X36: return "YUV12_1X36";
96// case MEDIA_BUS_FMT_YUV16_1X48: return "YUV16_1X48";
97// case MEDIA_BUS_FMT_UYYVYY16_0_5X48: return "UYYVYY16_0_5X48";
98 case MEDIA_BUS_FMT_SBGGR8_1X8: return "SBGGR8_1X8";
99 case MEDIA_BUS_FMT_SGBRG8_1X8: return "SGBRG8_1X8";
100 case MEDIA_BUS_FMT_SGRBG8_1X8: return "SGRBG8_1X8";
101 case MEDIA_BUS_FMT_SRGGB8_1X8: return "SRGGB8_1X8";
102 case MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8: return "SBGGR10_ALAW8_1X8";
103 case MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8: return "SGBRG10_ALAW8_1X8";
104 case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8: return "SGRBG10_ALAW8_1X8";
105 case MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8: return "SRGGB10_ALAW8_1X8";
106 case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: return "SBGGR10_DPCM8_1X8";
107 case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: return "SGBRG10_DPCM8_1X8";
108 case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: return "SGRBG10_DPCM8_1X8";
109 case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: return "SRGGB10_DPCM8_1X8";
110// case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_B: return "SBGGR10_2X8_PADHI_B";
111// case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_L: return "SBGGR10_2X8_PADHI_L";
112// case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_B: return "SBGGR10_2X8_PADLO_B";
113// case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_L: return "SBGGR10_2X8_PADLO_L";
114 case MEDIA_BUS_FMT_SBGGR10_1X10: return "SBGGR10_1X10";
115 case MEDIA_BUS_FMT_SGBRG10_1X10: return "SGBRG10_1X10";
116 case MEDIA_BUS_FMT_SGRBG10_1X10: return "SGRBG10_1X10";
117 case MEDIA_BUS_FMT_SRGGB10_1X10: return "SRGGB10_1X10";
118 case MEDIA_BUS_FMT_SBGGR12_1X12: return "SBGGR12_1X12";
119 case MEDIA_BUS_FMT_SGBRG12_1X12: return "SGBRG12_1X12";
120 case MEDIA_BUS_FMT_SGRBG12_1X12: return "SGRBG12_1X12";
121 case MEDIA_BUS_FMT_SRGGB12_1X12: return "SRGGB12_1X12";
122 case MEDIA_BUS_FMT_SBGGR14_1X14: return "SBGGR14_1X14";
123 case MEDIA_BUS_FMT_SGBRG14_1X14: return "SGBRG14_1X14";
124 case MEDIA_BUS_FMT_SGRBG14_1X14: return "SGRBG14_1X14";
125 case MEDIA_BUS_FMT_SRGGB14_1X14: return "SRGGB14_1X14";
126 case MEDIA_BUS_FMT_SBGGR16_1X16: return "SBGGR16_1X16";
127 case MEDIA_BUS_FMT_SGBRG16_1X16: return "SGBRG16_1X16";
128 case MEDIA_BUS_FMT_SGRBG16_1X16: return "SGRBG16_1X16";
129 case MEDIA_BUS_FMT_SRGGB16_1X16: return "SRGGB16_1X16";
130 case MEDIA_BUS_FMT_JPEG_1X8: return "JPEG_1X8";
131 case MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8: return "S5C_UYVY_JPEG_1X8";
132 case MEDIA_BUS_FMT_AHSV8888_1X32: return "AHSV8888_1X32";
133 default: return QString(code);
134 }
135}
136
137static QDebug operator<<(QDebug debug, const struct v4l2_mbus_framefmt &format)
138{
139 QDebugStateSaver saver(debug);
140 debug.nospace() << "v4l2_mbus_framefmt("
141 << "code: " << mediaBusFmtToString(format.code) << ", "
142 << "size: " << format.width << "x" << format.height << ")";
143 return debug;
144}
145
146static QDebug operator<<(QDebug debug, const struct v4l2_pix_format_mplane &format)
147{
148 QDebugStateSaver saver(debug);
149 debug.nospace() << "v4l2_pix_format_mplane("
150 << "pixel format: " << q_fourccToString(format.pixelformat) << ", "
151 << "size: " << format.width << "x" << format.height << ", "
152 << "planes: " << format.num_planes << ")";
153 return debug;
154}
155
157 : m_mediaDevice(media_device_new(devicePath.toStdString().c_str()))
158{
159 if (!m_mediaDevice)
160 qFatal("Couldn't get media device");
161
162 if (media_device_enumerate(m_mediaDevice))
163 qFatal("Couldn't enumerate media device");
164
165 m_info = media_get_info(m_mediaDevice);
166
167 qCDebug(qLcEglfsKmsDebug) << "Opened linux media device:"
168 << "\n\t Path:" << devicePath
169 << "\n\t Model:" << model()
170 << "\n\t Device name:" << deviceName();
171
172 resetLinks();
173}
174
176{
177 if (m_mediaDevice)
178 media_device_unref(m_mediaDevice);
179}
180
182{
183 return QString(m_info->model);
184}
185
187{
188 return QString(m_info->bus_info).split(":").last();
189}
190
192{
193 if (media_reset_links(m_mediaDevice)) {
194 qWarning() << "Could not reset media controller links.";
195 return false;
196 }
197 qCDebug(qLcEglfsKmsDebug) << "Reset media links";
198 return true;
199}
200
201struct media_link *QLinuxMediaDevice::parseLink(const QString &link)
202{
203 char *endp = nullptr;
204 struct media_link *mediaLink = media_parse_link(m_mediaDevice, link.toStdString().c_str(), &endp);
205
206 if (!mediaLink)
207 qWarning() << "Failed to parse media link:" << link;
208
209 return mediaLink;
210}
211
212struct media_pad *QLinuxMediaDevice::parsePad(const QString &pad)
213{
214 struct media_pad *mediaPad = media_parse_pad(m_mediaDevice, pad.toStdString().c_str(), nullptr);
215
216 if (!mediaPad)
217 qWarning() << "Failed to parse media pad:" << pad;
218
219 return mediaPad;
220}
221
222bool QLinuxMediaDevice::enableLink(struct media_link *link)
223{
224 if (media_setup_link(m_mediaDevice, link->source, link->sink, 1)) {
225 qWarning() << "Failed to enable media link.";
226 return false;
227 }
228 return true;
229}
230
231bool QLinuxMediaDevice::disableLink(struct media_link *link)
232{
233 if (media_setup_link(m_mediaDevice, link->source, link->sink, 0)) {
234 qWarning() << "Failed to disable media link.";
235 return false;
236 }
237 return true;
238}
239
240// Between the v4l-utils 1.10 and 1.12 releases, media_get_entity_by_name changed signature,
241// i.e. breaking source compatibility. Unfortunately the v4l-utils version is not exposed
242// through anything we can use through a regular ifdef so here we do a hack and create two
243// overloads for a function based on the signature of the function pointer argument. This
244// means that by calling safeGetEntity with media_get_entity_by_name as an argument, the
245// compiler will pick the correct version.
246//
247// v4l-utils v1.12 and later
248static struct media_entity *safeGetEntity(struct media_entity *(get_entity_by_name_fn)(struct media_device *, const char *),
249 struct media_device *device, const QString &name)
250{
251 return get_entity_by_name_fn(device, name.toStdString().c_str());
252}
253// v4l-utils v1.10 and earlier
254static struct media_entity *safeGetEntity(struct media_entity *(get_entity_by_name_fn)(struct media_device *, const char *, size_t),
255 struct media_device *device,
256 const QString &name)
257{
258 return get_entity_by_name_fn(device, name.toStdString().c_str(), name.length());
259}
260
261struct media_entity *QLinuxMediaDevice::getEntity(const QString &name)
262{
263 struct media_entity *entity = safeGetEntity(media_get_entity_by_name, m_mediaDevice, name);
264
265 if (!entity)
266 qWarning() << "Failed to get media entity:" << name;
267
268 return entity;
269}
270
272{
273 struct media_entity *entity = getEntity(name);
274 const char *deviceName = media_entity_get_devname(entity);
275 int fd = open(deviceName, O_RDWR);
276 qCDebug(qLcEglfsKmsDebug) << "Opened video device:" << deviceName << "with fd" << fd;
277 return fd;
278}
279
281 : m_subdevFd(mediaDevice->openVideoDevice(name))
282{
283}
284
286{
287 Q_ASSERT(size.isValid());
288 struct v4l2_format format;
289 memset(&format, 0, sizeof(struct v4l2_format));
290 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
291 if (ioctl(m_subdevFd, VIDIOC_G_FMT, &format) == -1) {
292 qErrnoWarning("VIDIOC_G_FMT for capture device failed");
293 return false;
294 }
295
296 format.fmt.pix_mp.width = static_cast<uint>(size.width());
297 format.fmt.pix_mp.height = static_cast<uint>(size.height());
298 format.fmt.pix_mp.field = V4L2_FIELD_NONE;
299 format.fmt.pix_mp.pixelformat = pixelFormat;
300 format.fmt.pix_mp.num_planes = 1;
301 format.fmt.pix_mp.flags = 0;
302 format.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
303 format.fmt.pix_mp.plane_fmt[0].sizeimage = 0;
304
305 if (ioctl(m_subdevFd, VIDIOC_S_FMT, &format) == -1) {
306 qWarning() << "Capture device" << m_subdevFd << "VIDIOC_S_FMT with format" << format.fmt.pix_mp
307 << "failed:" << strerror(errno);
308 return false;
309 }
310
311 qCDebug(qLcEglfsKmsDebug) << "Capture device" << m_subdevFd << "format set:" << format.fmt.pix_mp;
312 return true;
313}
314
316{
317 struct v4l2_requestbuffers requestBuffers;
318 memset(&requestBuffers, 0, sizeof(requestBuffers));
319 requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
320 requestBuffers.memory = V4L2_MEMORY_DMABUF;
321 requestBuffers.count = 0;
322 if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
323 qWarning("Capture device %d: VIDIOC_REQBUFS clear failed: %s", m_subdevFd, strerror(errno));
324 return false;
325 }
326 qCDebug(qLcEglfsKmsDebug, "Capture device %d: Deallocced buffers with REQBUF, now has %d buffers", m_subdevFd, requestBuffers.count);
327 Q_ASSERT(requestBuffers.count == 0);
328 return true;
329}
330
332{
333 struct v4l2_requestbuffers requestBuffers;
334 memset(&requestBuffers, 0, sizeof(requestBuffers));
335 requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
336 requestBuffers.memory = V4L2_MEMORY_DMABUF;
337 requestBuffers.count = 1;
338 if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
339 if (errno == EINVAL)
340 qWarning("Capture device %d: Multi-planar capture or dma buffers not supported", m_subdevFd);
341 qWarning("Capture device %d: VIDIOC_REQBUFS failed: %s", m_subdevFd, strerror(errno));
342 return false;
343 }
344 Q_ASSERT(requestBuffers.count == 1);
345 return true;
346}
347
348bool QLinuxMediaDevice::CaptureSubDevice::queueBuffer(int dmabufFd, const QSize &bufferSize)
349{
350 const uint numPlanes = 1;
351 struct v4l2_buffer buffer;
352 memset(&buffer, 0, sizeof(buffer));
353 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
354 buffer.memory = V4L2_MEMORY_DMABUF;
355 buffer.index = 0;
356
357 struct v4l2_plane planes[VIDEO_MAX_PLANES];
358 buffer.m.planes = planes;
359 buffer.length = numPlanes;
360 memset(planes, 0, sizeof(planes));
361 for (uint i = 0; i < numPlanes; i++) {
362 buffer.m.planes[i].m.fd = dmabufFd;
363 buffer.m.planes[i].length = static_cast<uint>(bufferSize.width() * bufferSize.height() * 4); //TODO: don't harcode bpp
364 buffer.m.planes[i].bytesused = static_cast<uint>(bufferSize.width() * bufferSize.height() * 4); //TODO: don't harcode bpp
365 }
366
367 if (ioctl(m_subdevFd, VIDIOC_QBUF, &buffer) == -1) {
368 qWarning("Capture device %d: VIDIOC_QBUF failed for dma buffer with fd %d: %s",
369 m_subdevFd, dmabufFd, strerror(errno));
370 return false;
371 }
372
373 return true;
374}
375
377{
378 const int numPlanes = 1;
379 struct v4l2_buffer buffer;
380 struct v4l2_plane planes[VIDEO_MAX_PLANES];
381
382 memset(&buffer, 0, sizeof(buffer));
383 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
384 buffer.memory = V4L2_MEMORY_DMABUF;
385 buffer.index = 0;
386 buffer.m.planes = planes;
387 buffer.length = numPlanes;
388 memset(planes, 0, sizeof(planes));
389
390 if (ioctl(m_subdevFd, VIDIOC_DQBUF, &buffer) == -1) {
391 qWarning("Capture device %d: VIDIOC_DQBUF failed: %s", m_subdevFd, strerror(errno));
392 return false;
393 }
394
395 return true;
396}
397
399{
400 return QLinuxMediaDevice::streamOn(m_subdevFd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
401}
402
404{
405 return QLinuxMediaDevice::streamOff(m_subdevFd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
406}
407
409 : m_subdevFd(mediaDevice->openVideoDevice(name))
410{
411}
412
413bool QLinuxMediaDevice::OutputSubDevice::setFormat(const QSize &size, uint32_t pixelFormat, uint32_t bytesPerLine)
414{
415 Q_ASSERT(size.isValid());
416 struct v4l2_format format;
417 memset(&format, 0, sizeof(struct v4l2_format));
418 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
419 if (ioctl(m_subdevFd, VIDIOC_G_FMT, &format) == -1) {
420 qErrnoWarning("VIDIOC_G_FMT for output device failed");
421 return false;
422 }
423
424 format.fmt.pix_mp.width = static_cast<uint>(size.width());
425 format.fmt.pix_mp.height = static_cast<uint>(size.height());
426 format.fmt.pix_mp.field = V4L2_FIELD_NONE;
427 format.fmt.pix_mp.pixelformat = pixelFormat;
428 format.fmt.pix_mp.num_planes = 1;
429 format.fmt.pix_mp.flags = 0;
430 format.fmt.pix_mp.plane_fmt[0].bytesperline = bytesPerLine;
431 format.fmt.pix_mp.plane_fmt[0].sizeimage = 0;
432
433 if (ioctl(m_subdevFd, VIDIOC_S_FMT, &format) == -1) {
434 qWarning() << "Output device" << m_subdevFd << "VIDIOC_S_FMT with format" << format.fmt.pix_mp
435 << "failed:" << strerror(errno);
436 return false;
437 }
438
439 qCDebug(qLcEglfsKmsDebug) << "Output device device" << m_subdevFd << "format set:" << format.fmt.pix_mp;
440 return true;
441}
442
444{
445 struct v4l2_requestbuffers requestBuffers;
446 memset(&requestBuffers, 0, sizeof(requestBuffers));
447 requestBuffers.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
448 requestBuffers.memory = V4L2_MEMORY_DMABUF;
449 requestBuffers.count = 0;
450 if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
451 qWarning("Output device %d: VIDIOC_REQBUFS clear failed: %s", m_subdevFd, strerror(errno));
452 return false;
453 }
454 qCDebug(qLcEglfsKmsDebug, "Output device %d: Deallocced buffers with REQBUF, now has %d buffers", m_subdevFd, requestBuffers.count);
455 Q_ASSERT(requestBuffers.count == 0);
456 return true;
457}
458
460{
461 struct v4l2_requestbuffers requestBuffers;
462 memset(&requestBuffers, 0, sizeof(requestBuffers));
463 requestBuffers.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
464 requestBuffers.memory = V4L2_MEMORY_DMABUF;
465 requestBuffers.count = 1;
466 if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
467 if (errno == EINVAL)
468 qWarning("Output device %d: Multi-planar output or dma buffers not supported", m_subdevFd);
469 qWarning("Output device %d: VIDIOC_REQBUFS failed: %s", m_subdevFd, strerror(errno));
470 return false;
471 }
472 qCDebug(qLcEglfsKmsDebug) << "REQBUF returned" << requestBuffers.count << "buffers for output device" << m_subdevFd;
473 return true;
474}
475
477{
478 const int numPlanes = 1;
479 struct v4l2_buffer buffer;
480 memset(&buffer, 0, sizeof(buffer));
481 buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
482 buffer.memory = V4L2_MEMORY_DMABUF;
483 buffer.index = 0;
484 buffer.length = numPlanes;
485 buffer.bytesused = bytesUsed;
486 buffer.field = V4L2_FIELD_NONE; //TODO: what is this?
487
488 struct v4l2_plane planes[numPlanes];
489 memset(planes, 0, sizeof(planes));
490 buffer.m.planes = planes;
491
492 for (int i = 0; i < numPlanes; i++) {
493 buffer.m.planes[i].m.fd = dmabufFd;
494 buffer.m.planes[i].length = length;
495 buffer.m.planes[i].bytesused = bytesUsed;
496 }
497
498 if (ioctl(m_subdevFd, VIDIOC_QBUF, &buffer) == -1) {
499 qWarning("Output device %d: VIDIOC_QBUF failed for dmabuf %d: %s", m_subdevFd, dmabufFd, strerror(errno));
500 return false;
501 }
502
503 if (!(buffer.flags & V4L2_BUF_FLAG_QUEUED)) {
504 qWarning() << "Queued flag not set on buffer for output device";
505 return false;
506 }
507
508 return true;
509}
510
512{
513 return QLinuxMediaDevice::streamOn(m_subdevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
514}
515
517{
518 return QLinuxMediaDevice::streamOff(m_subdevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
519}
520
522{
523 const char *deviceName = media_entity_get_devname(pad->entity);
524 int fd = open(deviceName, O_RDWR);
525 qCDebug(qLcEglfsKmsDebug) << "Opened video device:" << deviceName << "with fd" << fd;
526 return fd;
527}
528
529bool QLinuxMediaDevice::setSubdevFormat(struct media_pad *pad, const QSize &size, uint32_t mbusFormat)
530{
531 Q_ASSERT(size.isValid());
532 struct v4l2_mbus_framefmt format;
533 format.width = static_cast<uint>(size.width());
534 format.height = static_cast<uint>(size.height());
535 format.code = mbusFormat;
536
537 if (v4l2_subdev_set_format(pad->entity, &format, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE)) {
538 qWarning() << "Setting v4l2_subdev_set_format failed for format" << format;
539 return false;
540 }
541
542 if (format.code != mbusFormat) {
543 qWarning() << "Got" << mediaBusFmtToString(format.code) << "instead of"
544 << mediaBusFmtToString(mbusFormat) << "when setting subdevice format";
545 return false;
546 }
547
548 qCDebug(qLcEglfsKmsDebug) << "Set format to" << format << "for entity" << pad->entity << "index" << pad->index;
549 return true;
550}
551
553{
554 struct v4l2_control control;
555 control.id = V4L2_CID_ALPHA_COMPONENT;
556 control.value = static_cast<__s32>(alpha * 0xff);
557 if (ioctl(subdevFd, VIDIOC_S_CTRL, &control) == -1) {
558 qErrnoWarning("Setting alpha (%d) failed", control.value);
559 return false;
560 }
561 return true;
562}
563
564bool QLinuxMediaDevice::setSubdevSelection(struct media_pad *pad, const QRect &geometry, uint target)
565{
566 Q_ASSERT(geometry.isValid());
567 struct v4l2_rect rect;
568 rect.left = geometry.left();
569 rect.top = geometry.top();
570 rect.width = static_cast<uint>(geometry.width());
571 rect.height = static_cast<uint>(geometry.height());
572
573 int ret = v4l2_subdev_set_selection(pad->entity, &rect, pad->index, target, V4L2_SUBDEV_FORMAT_ACTIVE);
574 if (ret) {
575 qWarning() << "Setting subdev selection failed.";
576 return false;
577 }
578
579 return true;
580}
581
582bool QLinuxMediaDevice::setSubdevCrop(struct media_pad *pad, const QRect &geometry)
583{
584 return setSubdevSelection(pad, geometry, V4L2_SEL_TGT_CROP);
585}
586
587bool QLinuxMediaDevice::setSubdevCompose(struct media_pad *pad, const QRect &geometry)
588{
589 return setSubdevSelection(pad, geometry, V4L2_SEL_TGT_COMPOSE);
590}
591
592bool QLinuxMediaDevice::streamOn(int subDeviceFd, v4l2_buf_type bufferType)
593{
594 if (ioctl(subDeviceFd, VIDIOC_STREAMON, &bufferType) == -1) {
595 qWarning("VIDIOC_STREAMON failed for subdevice %d: %s", subDeviceFd, strerror(errno));
596 return false;
597 }
598
599 return true;
600}
601
602bool QLinuxMediaDevice::streamOff(int subDeviceFd, v4l2_buf_type bufferType)
603{
604 if (ioctl(subDeviceFd, VIDIOC_STREAMOFF, &bufferType) == -1) {
605 qWarning("VIDIOC_STREAMOFF failed for subdevice %d: %s", subDeviceFd, strerror(errno));
606 return false;
607 }
608
609 return true;
610}
611
IOBluetoothDevice * device
\inmodule QtCore
\inmodule QtCore
bool setFormat(const QSize &size, uint32_t pixelFormat=V4L2_PIX_FMT_ABGR32)
CaptureSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name)
bool queueBuffer(int dmabufFd, const QSize &bufferSize)
bool queueBuffer(int dmabufFd, uint bytesUsed, uint length)
OutputSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name)
bool setFormat(const QSize &size, uint32_t pixelFormat, uint32_t bytesPerLine)
int openVideoDevice(const QString &name)
struct media_link * parseLink(const QString &link)
bool enableLink(struct media_link *link)
static bool setSubdevCrop(struct media_pad *pad, const QRect &geometry)
bool disableLink(struct media_link *link)
static bool setSubdevSelection(struct media_pad *pad, const QRect &geometry, uint target)
struct media_pad * parsePad(const QString &pad)
static bool setSubdevAlpha(int subdevFd, qreal alpha)
struct media_entity * getEntity(const QString &name)
QLinuxMediaDevice(const QString &devicePath)
static bool setSubdevFormat(struct media_pad *pad, const QSize &size, uint32_t mbusFormat=MEDIA_BUS_FMT_ARGB8888_1X32)
static bool setSubdevCompose(struct media_pad *pad, const QRect &geometry)
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
Definition qrect.h:170
constexpr int top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:176
constexpr int left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:173
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
std::string toStdString() const
Returns a std::string object with the data contained in this QString.
Definition qstring.h:1444
rect
[4]
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE QString q_fourccToString(uint code)
static QDebug operator<<(QDebug debug, const struct v4l2_mbus_framefmt &format)
static QT_BEGIN_NAMESPACE QString mediaBusFmtToString(uint code)
static struct media_entity * safeGetEntity(struct media_entity *(get_entity_by_name_fn)(struct media_device *, const char *), struct media_device *device, const QString &name)
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLuint buffer
GLenum target
GLuint64 GLenum GLint fd
GLuint name
GLint GLsizei GLsizei GLenum format
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
file open(QIODevice::ReadOnly)