Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qvsp2blendingdevice.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// Qt-Security score:significant reason:default
4
6#include <qeglfskmshelpers_p.h>
7
8#include <QDebug>
9#include <QtCore/QLoggingCategory>
10
11#include <drm_fourcc.h>
12
14
15Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
16
17//TODO: is this the right place for these conversion functions?
18static inline uint drmFormatToV4l2PixelFormat(uint drmFormat) {
19 //ARGB8888 == ABGR32 because linux media list stuff in the opposite order, but the fourcc is the same
20 Q_ASSERT(DRM_FORMAT_ARGB8888 == V4L2_PIX_FMT_ABGR32);
21 return drmFormat;
22}
23
24static uint drmFormatToMediaBusFormat(uint drmFormat)
25{
26 switch (drmFormat) {
27 case DRM_FORMAT_RGB888:
28 return MEDIA_BUS_FMT_RGB888_1X24;
29 case DRM_FORMAT_BGR888:
30 return MEDIA_BUS_FMT_BGR888_1X24;
31 case DRM_FORMAT_XRGB8888:
32 case DRM_FORMAT_XBGR8888:
33// return MEDIA_BUS_FMT_RGB888_1X32_PADHI; // doesn't work on renesas m3, just use fallthrough to argb for now
34 case DRM_FORMAT_RGBX8888:
35 case DRM_FORMAT_BGRX8888:
36 case DRM_FORMAT_ARGB8888:
37 case DRM_FORMAT_ABGR8888:
38 case DRM_FORMAT_RGBA8888:
39 case DRM_FORMAT_BGRA8888:
40 return MEDIA_BUS_FMT_ARGB8888_1X32;
41 default:
42 qWarning() << "Unknown drm format" << q_fourccToString(drmFormat) << "defaulting to argb8888";
43 return MEDIA_BUS_FMT_ARGB8888_1X32;
44 }
45}
46
48 : m_mediaDevice("/dev/media0")
50{
51 QLinuxMediaDevice &md = m_mediaDevice;
52 QString deviceName = md.deviceName();
53
54 if (md.model() != QString("VSP2"))
55 qWarning() << "Unsupported media device model:" << md.model();
56
57 if (deviceName != "fe960000.vsp")
58 qWarning() << "Unknown media device name:" << deviceName;
59
60 const int numInputs = 5;
61
62 for (int i = 0; i < numInputs; ++i) {
63 Input input;
64 input.linkToBru = md.parseLink(QString("'%1 rpf.%2':1 -> '%1 bru':%2").arg(deviceName).arg(i));
65 input.inputFormatPad = md.parsePad(QString("'%1 rpf.%2':0").arg(deviceName).arg(i));
66 input.outputFormatPad = md.parsePad(QString("'%1 rpf.%2':1").arg(deviceName).arg(i));
67 input.outputFormatFd = QLinuxMediaDevice::openVideoDevice(input.outputFormatPad);
68 input.bruInputFormatPad = md.parsePad(QString("'%1 bru':%2").arg(deviceName).arg(i));
69 input.rpfInput = new QLinuxMediaDevice::OutputSubDevice(&md, QString("%1 rpf.%2 input").arg(deviceName).arg(i));
70 m_inputs.append(input);
71 }
72
73 m_wpfOutput = new QLinuxMediaDevice::CaptureSubDevice(&md, QString("%1 wpf.0 output").arg(deviceName));
74
75 // Setup links for output
76 md.enableLink(md.parseLink(QString("'%1 bru':5 -> '%1 wpf.0':0").arg(deviceName)));
77 md.enableLink(md.parseLink(QString("'%1 wpf.0':1 -> '%1 wpf.0 output':0").arg(deviceName)));
78
79 // Output pads
80 auto bruOutputFormatPad = md.parsePad(QString("'%1 bru':5").arg(deviceName));
81 auto wpfInputFormatPad = md.parsePad(QString("'%1 wpf.0':0").arg(deviceName));
82 auto wpfOutputFormatPad = md.parsePad(QString("'%1 wpf.0':1").arg(deviceName));
83
84 m_wpfOutput->setFormat(screenSize);
85 QLinuxMediaDevice::setSubdevFormat(bruOutputFormatPad, screenSize);
86 QLinuxMediaDevice::setSubdevFormat(wpfInputFormatPad, screenSize);
87 QLinuxMediaDevice::setSubdevFormat(wpfOutputFormatPad, screenSize);
88
89 m_wpfOutput->requestBuffer();
90}
91
92bool QVsp2BlendingDevice::enableInput(int i, const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
93{
94 qCDebug(qLcEglfsKmsDebug) << "Blend unit: Enabling input" << i;
95 if (m_inputs[i].enabled) {
96 qWarning("Vsp2: Input %d already enabled", i);
97 return false;
98 }
99
100 if (!bufferGeometry.isValid()) { //TODO: bounds checking as well?
101 qWarning() << "Vsp2: Invalid buffer geometry";
102 return false;
103 }
104
105 Input &input = m_inputs[i];
106 if (!m_mediaDevice.enableLink(input.linkToBru))
107 return false;
108
109 uint pixelFormat = drmFormatToV4l2PixelFormat(drmFormat);
110 if (!setInputFormat(i, bufferGeometry, pixelFormat, bytesPerLine)) {
112 return false;
113 }
114
115 input.rpfInput->requestBuffer();
116 return true;
117}
118
119int QVsp2BlendingDevice::enableInput(const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
120{
121 for (int i = 0; i < m_inputs.size(); ++i) {
122 if (!m_inputs[i].enabled)
123 return enableInput(i, bufferGeometry, drmFormat, bytesPerLine) ? i : -1;
124 }
125 qWarning() << "Vsp2: No more inputs available in blend unit";
126 return -1;
127}
128
130{
131 qCDebug(qLcEglfsKmsDebug) << "Vsp2: disabling input" << i;
132 if (!m_inputs[i].enabled) {
133 qWarning("Vsp2: Input %d already disabled", i);
134 return false;
135 }
136 m_mediaDevice.disableLink(m_inputs[i].linkToBru);
137 m_inputs[i].rpfInput->clearBuffers();
138 m_inputs[i].enabled = false;
139 return true;
140}
141
142bool QVsp2BlendingDevice::setInputBuffer(int index, int dmabufFd)
143{
144 Input &input = m_inputs[index];
145
146 if (!input.enabled) {
147 qWarning() << "Vsp2: Can't queue on disabled input" << index;
148 return false;
149 }
150
151 // Don't queue the buffer yet, store it here and wait until blending
152 if (input.dmabuf.fd != dmabufFd) {
153 m_dirty = true;
154 input.dmabuf.fd = dmabufFd;
155 }
156 return true;
157}
158
159bool QVsp2BlendingDevice::setInputPosition(int index, const QPoint &position)
160{
161 Input &input = m_inputs[index];
162
163 if (input.geometry.topLeft() == position)
164 return true;
165
166 m_dirty = true;
167 input.geometry.moveTopLeft(position);
168 return QLinuxMediaDevice::setSubdevCompose(input.bruInputFormatPad, input.geometry);
169}
170
171bool QVsp2BlendingDevice::setInputAlpha(int index, qreal alpha)
172{
173 Input &input = m_inputs[index];
174 if (input.alpha == alpha)
175 return true;
176
177 m_dirty = true;
178 input.alpha = alpha;
179 return QLinuxMediaDevice::setSubdevAlpha(input.outputFormatFd, alpha);
180}
181
182bool QVsp2BlendingDevice::blend(int outputDmabufFd)
183{
184 if (!m_dirty)
185 qWarning("Blending without being dirty, should not be necessary");
186
188 qWarning("Vsp2: Can't blend when layers are not enabled in order from 0 without gaps.");
189 return false;
190 }
191
192 bool queueingFailed = false;
193 // Queue dma input buffers
194 for (int i=0; i < m_inputs.size(); ++i) {
195 auto &input = m_inputs[i];
196 if (input.enabled) {
197 if (!input.rpfInput->queueBuffer(input.dmabuf.fd, input.dmabuf.bytesUsed, input.dmabuf.length)) {
198 qWarning() << "Vsp2: Failed to queue buffer for input" << i
199 << "with dmabuf" << input.dmabuf.fd
200 << "and size" << input.geometry.size();
201
202 queueingFailed = true;
203 }
204 }
205 }
206
207 if (queueingFailed) {
208 qWarning() << "Vsp2: Trying to clean up queued buffers";
209 for (auto &input : std::as_const(m_inputs)) {
210 if (input.enabled) {
211 if (!input.rpfInput->clearBuffers())
212 qWarning() << "Vsp2: Failed to remove buffers after an aborted blend";
213 }
214 }
215 return false;
216 }
217
218 if (!m_wpfOutput->queueBuffer(outputDmabufFd, m_screenSize)) {
219 qWarning() << "Vsp2: Failed to queue blending output buffer" << outputDmabufFd << m_screenSize;
220 return false;
221 }
222
223 if (!streamOn()) {
224 qWarning() << "Vsp2: Failed to start streaming";
225 return false;
226 }
227
228 if (!m_wpfOutput->dequeueBuffer()) {
229 qWarning() << "Vsp2: Failed to dequeue blending output buffer";
230 if (!streamOff())
231 qWarning() << "Vsp2: Failed to stop streaming when recovering after a broken blend.";
232 return false;
233 }
234
235 if (!streamOff()) {
236 qWarning() << "Vsp2: Failed to stop streaming";
237 return false;
238 }
239
240 m_dirty = false;
241 return true;
242}
243
245{
246 return m_inputs.size();
247}
248
250{
251 bool seenDisabled = false;
252 for (auto &input : std::as_const(m_inputs)) {
253 if (seenDisabled && input.enabled)
254 return false;
255 seenDisabled |= !input.enabled;
256 }
257 return m_inputs[0].enabled;
258}
259
260bool QVsp2BlendingDevice::streamOn()
261{
262 for (auto &input : m_inputs) {
263 if (input.enabled) {
264 if (!input.rpfInput->streamOn()) {
265 //TODO: perhaps it's better to try to continue with the other inputs?
266 return false;
267 }
268 }
269 }
270
271 return m_wpfOutput->streamOn();
272}
273
274bool QVsp2BlendingDevice::streamOff()
275{
276 bool succeeded = m_wpfOutput->streamOff();
277 for (auto &input : m_inputs) {
278 if (input.enabled)
279 succeeded &= input.rpfInput->streamOff();
280 }
281 return succeeded;
282}
283
284bool QVsp2BlendingDevice::setInputFormat(int i, const QRect &bufferGeometry, uint pixelFormat, uint bytesPerLine)
285{
286 Input &input = m_inputs[i];
287
288 Q_ASSERT(bufferGeometry.isValid());
289
290 const uint bpp = 4; //TODO: don't hardcode bpp, get it from pixelFormat?
291 input.enabled = true;
292 input.geometry = bufferGeometry;
293 input.dmabuf.bytesUsed = bpp * static_cast<uint>(bufferGeometry.width()) * static_cast<uint>(bufferGeometry.height());
294 input.dmabuf.length = static_cast<uint>(bufferGeometry.height()) * bytesPerLine;
295
296 const QSize size = bufferGeometry.size();
297
298 if (!input.rpfInput->setFormat(size, pixelFormat, bytesPerLine)) // rpf.x input
299 return false;
300
301 const uint mediaBusFormat = drmFormatToMediaBusFormat(pixelFormat);
302 if (!QLinuxMediaDevice::setSubdevFormat(input.inputFormatPad, size, mediaBusFormat)) // rpf.x:0
303 return false;
304
305 if (!QLinuxMediaDevice::setSubdevFormat(input.outputFormatPad, size, mediaBusFormat)) // rpf.x:1
306 return false;
307
308 if (!QLinuxMediaDevice::setSubdevFormat(input.bruInputFormatPad, size, mediaBusFormat)) // bru:x
309 return false;
310
311 if (!QLinuxMediaDevice::setSubdevCrop(input.inputFormatPad, QRect(QPoint(0, 0), size)))
312 return false;
313
314 if (!QLinuxMediaDevice::setSubdevCompose(input.bruInputFormatPad, bufferGeometry))
315 return false;
316
317 return true;
318}
319
320QT_END_NAMESPACE
bool queueBuffer(int dmabufFd, const QSize &bufferSize)
bool enableLink(struct media_link *link)
static bool setSubdevCrop(struct media_pad *pad, const QRect &geometry)
bool disableLink(struct media_link *link)
static int openVideoDevice(media_pad *pad)
static bool setSubdevCompose(struct media_pad *pad, const QRect &geometry)
QVsp2BlendingDevice(const QSize &screenSize)
int enableInput(const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
bool blend(int outputDmabufFd)
bool setInputBuffer(int index, int dmabufFd)
bool enableInput(int i, const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
bool setInputPosition(int index, const QPoint &position)
bool setInputAlpha(int index, qreal alpha)
Combined button and popup list for selecting options.
static uint drmFormatToMediaBusFormat(uint drmFormat)