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