Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qvideoframeconverter.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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#include "qvideoframeformat.h"
7
8#include <QtCore/qcoreapplication.h>
9#include <QtCore/qsize.h>
10#include <QtCore/qhash.h>
11#include <QtCore/qfile.h>
12#include <QtCore/qthreadstorage.h>
13#include <QtGui/qimage.h>
14#include <QtGui/qoffscreensurface.h>
15#include <qpa/qplatformintegration.h>
16#include <private/qvideotexturehelper_p.h>
17#include <private/qabstractvideobuffer_p.h>
18#include <private/qguiapplication_p.h>
19#include <rhi/qrhi.h>
20
21#ifdef Q_OS_DARWIN
22#include <QtCore/private/qcore_mac_p.h>
23#endif
24
26
27static Q_LOGGING_CATEGORY(qLcVideoFrameConverter, "qt.multimedia.video.frameconverter")
28
29namespace {
30
31struct State
32{
33 QRhi *rhi = nullptr;
34#if QT_CONFIG(opengl)
35 QOffscreenSurface *fallbackSurface = nullptr;
36#endif
37 bool cpuOnly = false;
38 ~State() {
39 delete rhi;
40#if QT_CONFIG(opengl)
41 delete fallbackSurface;
42#endif
43 }
44};
45
46}
47
50
51static const float g_quad[] = {
52 // Rotation 0 CW
53 1.f, -1.f, 1.f, 1.f,
54 1.f, 1.f, 1.f, 0.f,
55 -1.f, -1.f, 0.f, 1.f,
56 -1.f, 1.f, 0.f, 0.f,
57 // Rotation 90 CW
58 1.f, -1.f, 1.f, 0.f,
59 1.f, 1.f, 0.f, 0.f,
60 -1.f, -1.f, 1.f, 1.f,
61 -1.f, 1.f, 0.f, 1.f,
62 // Rotation 180 CW
63 1.f, -1.f, 0.f, 0.f,
64 1.f, 1.f, 0.f, 1.f,
65 -1.f, -1.f, 1.f, 0.f,
66 -1.f, 1.f, 1.f, 1.f,
67 // Rotation 270 CW
68 1.f, -1.f, 0.f, 1.f,
69 1.f, 1.f, 1.f, 1.f,
70 -1.f, -1.f, 0.f, 0.f,
71 -1.f, 1.f, 1.f, 0.f,
72};
73
75{
76 switch (format) {
85 return true;
86 default:
87 return false;
88 }
89};
90
92{
94 if (shader.isValid())
95 return shader;
96
97 QFile f(name);
98 if (f.open(QIODevice::ReadOnly))
99 shader = QShader::fromSerialized(f.readAll());
100
101 if (shader.isValid())
103
104 return shader;
105}
106
108 bool mirrorX, bool mirrorY)
109{
111 if (mirrorX)
112 t.scale(-1.f, 1.f);
113 if (rotation != QVideoFrame::Rotation0)
114 t.rotate(float(rotation));
115 if (mirrorY)
116 t.scale(1.f, -1.f);
117 if (!t.isIdentity())
118 image = image.transformed(t);
119}
120
121static void imageCleanupHandler(void *info)
122{
123 QByteArray *imageData = reinterpret_cast<QByteArray *>(info);
124 delete imageData;
125}
126
127static QRhi *initializeRHI(QRhi *videoFrameRhi)
128{
129 if (g_state.localData().rhi || g_state.localData().cpuOnly)
130 return g_state.localData().rhi;
131
132 QRhi::Implementation backend = videoFrameRhi ? videoFrameRhi->backend() : QRhi::Null;
133
135
136#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
137 if (backend == QRhi::Metal || backend == QRhi::Null) {
139 g_state.localData().rhi = QRhi::create(QRhi::Metal, &params);
140 }
141#endif
142
143#if defined(Q_OS_WIN)
144 if (backend == QRhi::D3D11 || backend == QRhi::Null) {
146 g_state.localData().rhi = QRhi::create(QRhi::D3D11, &params);
147 }
148#endif
149
150#if QT_CONFIG(opengl)
151 if (!g_state.localData().rhi && (backend == QRhi::OpenGLES2 || backend == QRhi::Null)) {
155
156 g_state.localData().fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
158 params.fallbackSurface = g_state.localData().fallbackSurface;
159 if (backend == QRhi::OpenGLES2)
160 params.shareContext = static_cast<const QRhiGles2NativeHandles*>(videoFrameRhi->nativeHandles())->context;
161 g_state.localData().rhi = QRhi::create(QRhi::OpenGLES2, &params);
162 }
163 }
164#endif
165 }
166
167 if (!g_state.localData().rhi) {
168 g_state.localData().cpuOnly = true;
169 qWarning() << Q_FUNC_INFO << ": No RHI backend. Using CPU conversion.";
170 }
171
172 return g_state.localData().rhi;
173}
174
175static bool updateTextures(QRhi *rhi,
176 std::unique_ptr<QRhiBuffer> &uniformBuffer,
177 std::unique_ptr<QRhiSampler> &textureSampler,
178 std::unique_ptr<QRhiShaderResourceBindings> &shaderResourceBindings,
179 std::unique_ptr<QRhiGraphicsPipeline> &graphicsPipeline,
180 std::unique_ptr<QRhiRenderPassDescriptor> &renderPass,
182 const std::unique_ptr<QVideoFrameTextures> &videoFrameTextures)
183{
184 auto format = frame.surfaceFormat();
185 auto pixelFormat = format.pixelFormat();
186
187 auto textureDesc = QVideoTextureHelper::textureDescription(pixelFormat);
188
189 QRhiShaderResourceBinding bindings[4];
190 auto *b = bindings;
192 uniformBuffer.get());
193 for (int i = 0; i < textureDesc->nplanes; ++i)
195 videoFrameTextures->texture(i), textureSampler.get());
196 shaderResourceBindings->setBindings(bindings, b);
197 shaderResourceBindings->create();
198
199 graphicsPipeline.reset(rhi->newGraphicsPipeline());
200 graphicsPipeline->setTopology(QRhiGraphicsPipeline::TriangleStrip);
201
203 if (!vs.isValid())
204 return false;
205
207 if (!fs.isValid())
208 return false;
209
210 graphicsPipeline->setShaderStages({
213 });
214
215 QRhiVertexInputLayout inputLayout;
216 inputLayout.setBindings({
217 { 4 * sizeof(float) }
218 });
219 inputLayout.setAttributes({
221 { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
222 });
223
224 graphicsPipeline->setVertexInputLayout(inputLayout);
225 graphicsPipeline->setShaderResourceBindings(shaderResourceBindings.get());
226 graphicsPipeline->setRenderPassDescriptor(renderPass.get());
227 graphicsPipeline->create();
228
229 return true;
230}
231
232static QImage convertJPEG(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
233{
234 QVideoFrame varFrame = frame;
235 if (!varFrame.map(QVideoFrame::ReadOnly)) {
236 qCDebug(qLcVideoFrameConverter) << Q_FUNC_INFO << ": frame mapping failed";
237 return {};
238 }
240 image.loadFromData(varFrame.bits(0), varFrame.mappedBytes(0), "JPG");
241 varFrame.unmap();
242 rasterTransform(image, rotation, mirrorX, mirrorY);
243 return image;
244}
245
246static QImage convertCPU(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
247{
248 VideoFrameConvertFunc convert = qConverterForFormat(frame.pixelFormat());
249 if (!convert) {
250 qCDebug(qLcVideoFrameConverter) << Q_FUNC_INFO << ": unsupported pixel format" << frame.pixelFormat();
251 return {};
252 } else {
253 QVideoFrame varFrame = frame;
254 if (!varFrame.map(QVideoFrame::ReadOnly)) {
255 qCDebug(qLcVideoFrameConverter) << Q_FUNC_INFO << ": frame mapping failed";
256 return {};
257 }
259 QImage image = QImage(varFrame.width(), varFrame.height(), format);
260 convert(varFrame, image.bits());
261 varFrame.unmap();
262 rasterTransform(image, rotation, mirrorX, mirrorY);
263 return image;
264 }
265}
266
267QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
268{
269#ifdef Q_OS_DARWIN
270 QMacAutoReleasePool releasePool;
271#endif
272
273 if (!g_state.hasLocalData())
274 g_state.setLocalData({});
275
276 std::unique_ptr<QRhiRenderPassDescriptor> renderPass;
277 std::unique_ptr<QRhiBuffer> vertexBuffer;
278 std::unique_ptr<QRhiBuffer> uniformBuffer;
279 std::unique_ptr<QRhiTexture> targetTexture;
280 std::unique_ptr<QRhiTextureRenderTarget> renderTarget;
281 std::unique_ptr<QRhiSampler> textureSampler;
282 std::unique_ptr<QRhiShaderResourceBindings> shaderResourceBindings;
283 std::unique_ptr<QRhiGraphicsPipeline> graphicsPipeline;
284
285 if (frame.size().isEmpty() || frame.pixelFormat() == QVideoFrameFormat::Format_Invalid)
286 return {};
287
288 if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg)
289 return convertJPEG(frame, rotation, mirrorX, mirrorY);
290
291 QRhi *rhi = nullptr;
292
293 if (frame.videoBuffer())
294 rhi = frame.videoBuffer()->rhi();
295
296 if (!rhi || rhi->thread() != QThread::currentThread())
297 rhi = initializeRHI(rhi);
298
299 if (!rhi || rhi->isRecordingFrame())
300 return convertCPU(frame, rotation, mirrorX, mirrorY);
301
302 // Do conversion using shaders
303
304 const int rotationIndex = (rotation / 90) % 4;
305
307 if (rotationIndex % 2)
309
310 vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(g_quad)));
311 vertexBuffer->create();
312
313 uniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 64 + 4 + 4 + 4 + 4));
314 uniformBuffer->create();
315
318 textureSampler->create();
319
320 shaderResourceBindings.reset(rhi->newShaderResourceBindings());
321
323 if (!targetTexture->create()) {
324 qCDebug(qLcVideoFrameConverter) << "Failed to create target texture. Using CPU conversion.";
325 return convertCPU(frame, rotation, mirrorX, mirrorY);
326 }
327
328 renderTarget.reset(rhi->newTextureRenderTarget({ { targetTexture.get() } }));
329 renderPass.reset(renderTarget->newCompatibleRenderPassDescriptor());
330 renderTarget->setRenderPassDescriptor(renderPass.get());
331 renderTarget->create();
332
333 QRhiCommandBuffer *cb = nullptr;
334 QRhi::FrameOpResult r = rhi->beginOffscreenFrame(&cb);
335 if (r != QRhi::FrameOpSuccess) {
336 qCDebug(qLcVideoFrameConverter) << "Failed to set up offscreen frame. Using CPU conversion.";
337 return convertCPU(frame, rotation, mirrorX, mirrorY);
338 }
339
340 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
341
342 rub->uploadStaticBuffer(vertexBuffer.get(), g_quad);
343
344 QVideoFrame frameTmp = frame;
345 auto videoFrameTextures = QVideoTextureHelper::createTextures(frameTmp, rhi, rub, {});
346 if (!videoFrameTextures) {
347 qCDebug(qLcVideoFrameConverter) << "Failed obtain textures. Using CPU conversion.";
348 return convertCPU(frame, rotation, mirrorX, mirrorY);
349 }
350
351 if (!updateTextures(rhi, uniformBuffer, textureSampler, shaderResourceBindings,
352 graphicsPipeline, renderPass, frameTmp, videoFrameTextures)) {
353 qCDebug(qLcVideoFrameConverter) << "Failed to update textures. Using CPU conversion.";
354 return convertCPU(frame, rotation, mirrorX, mirrorY);
355 }
356
357 float xScale = mirrorX ? -1.0 : 1.0;
358 float yScale = mirrorY ? -1.0 : 1.0;
359
360 if (rhi->isYUpInFramebuffer())
361 yScale = -yScale;
362
364 transform.scale(xScale, yScale);
365
366 QByteArray uniformData(64 + 64 + 4 + 4, Qt::Uninitialized);
367 QVideoTextureHelper::updateUniformData(&uniformData, frame.surfaceFormat(), frame, transform, 1.f);
368 rub->updateDynamicBuffer(uniformBuffer.get(), 0, uniformData.size(), uniformData.constData());
369
370 cb->beginPass(renderTarget.get(), Qt::black, { 1.0f, 0 }, rub);
371 cb->setGraphicsPipeline(graphicsPipeline.get());
372
373 cb->setViewport({ 0, 0, float(frameSize.width()), float(frameSize.height()) });
374 cb->setShaderResources(shaderResourceBindings.get());
375
376 quint32 vertexOffset = quint32(sizeof(float)) * 16 * rotationIndex;
377 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer.get(), vertexOffset);
378 cb->setVertexInput(0, 1, &vbufBinding);
379 cb->draw(4);
380
381 QRhiReadbackDescription readDesc(targetTexture.get());
382 QRhiReadbackResult readResult;
383 bool readCompleted = false;
384
385 readResult.completed = [&readCompleted] { readCompleted = true; };
386
387 rub = rhi->nextResourceUpdateBatch();
388 rub->readBackTexture(readDesc, &readResult);
389
390 cb->endPass(rub);
391
392 rhi->endOffscreenFrame();
393
394 if (!readCompleted) {
395 qCDebug(qLcVideoFrameConverter) << "Failed to read back texture. Using CPU conversion.";
396 return convertCPU(frame, rotation, mirrorX, mirrorY);
397 }
398
399 QByteArray *imageData = new QByteArray(readResult.data);
400
401 return QImage(reinterpret_cast<const uchar *>(imageData->constData()),
402 readResult.pixelSize.width(), readResult.pixelSize.height(),
404}
405
407
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
static bool testAttribute(Qt::ApplicationAttribute attribute)
Returns true if attribute attribute is set; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
static QPlatformIntegration * platformIntegration()
\inmodule QtCore
Definition qhash.h:818
\inmodule QtGui
Definition qimage.h:37
@ Format_RGB32
Definition qimage.h:46
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtGui
@ Immutable
Definition qrhi.h:837
@ Dynamic
Definition qrhi.h:839
@ VertexBuffer
Definition qrhi.h:843
@ UniformBuffer
Definition qrhi.h:845
\inmodule QtGui
Definition qrhi.h:1614
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1643
\inmodule QtGui
\inmodule QtGui
\variable QRhiGles2InitParams::format
\inmodule QtRhi
\inmodule QtGui
Definition qrhi.h:765
\inmodule QtGui
Definition qrhi.h:1694
void updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
Enqueues updating a region of a QRhiBuffer buf created with the type QRhiBuffer::Dynamic.
Definition qrhi.cpp:8595
void uploadStaticBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
Enqueues updating a region of a QRhiBuffer buf created with the type QRhiBuffer::Immutable or QRhiBuf...
Definition qrhi.cpp:8615
void readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
Enqueues a texture-to-host copy operation as described by rb.
Definition qrhi.cpp:8788
@ ClampToEdge
Definition qrhi.h:1017
\inmodule QtGui
Definition qrhi.h:431
static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
Definition qrhi.cpp:5406
static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf)
Definition qrhi.cpp:5292
@ RenderTarget
Definition qrhi.h:886
\inmodule QtGui
Definition qrhi.h:313
void setBindings(std::initializer_list< QRhiVertexInputBinding > list)
Sets the bindings from the specified list.
Definition qrhi.h:317
void setAttributes(std::initializer_list< QRhiVertexInputAttribute > list)
Sets the attributes from the specified list.
Definition qrhi.h:329
\inmodule QtGui
Definition qrhi.h:1767
QRhiBuffer * newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
Definition qrhi.cpp:10079
QThread * thread() const
Definition qrhi.cpp:8428
QRhiShaderResourceBindings * newShaderResourceBindings()
Definition qrhi.cpp:10060
Implementation backend() const
Definition qrhi.cpp:8289
Implementation
Describes which graphics API-specific backend gets used by a QRhi instance.
Definition qrhi.h:1769
@ Metal
Definition qrhi.h:1774
@ Null
Definition qrhi.h:1770
@ D3D11
Definition qrhi.h:1773
@ OpenGLES2
Definition qrhi.h:1772
QRhiSampler * newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode addressU, QRhiSampler::AddressMode addressV, QRhiSampler::AddressMode addressW=QRhiSampler::Repeat)
Definition qrhi.cpp:10228
QRhiTextureRenderTarget * newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags={})
Definition qrhi.cpp:10245
bool isRecordingFrame() const
Definition qrhi.cpp:10365
QRhiGraphicsPipeline * newGraphicsPipeline()
Definition qrhi.cpp:10037
static QRhi * create(Implementation impl, QRhiInitParams *params, Flags flags={}, QRhiNativeHandles *importDevice=nullptr)
Definition qrhi.cpp:8129
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10133
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:9708
FrameOpResult
Describes the result of operations that can have a soft failure.
Definition qrhi.h:1786
@ FrameOpSuccess
Definition qrhi.h:1787
\inmodule QtGui
Definition qshader.h:81
static QShader fromSerialized(const QByteArray &data)
Creates a new QShader instance from the given data.
Definition qshader.cpp:510
bool isValid() const
Definition qshader.cpp:313
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:123
void transpose() noexcept
Swaps the width and height values.
Definition qsize.cpp:130
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
\inmodule QtCore
static QThread * currentThread()
Definition qthread.cpp:966
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
PixelFormat
Enumerates video data types.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:26
RotationAngle
The angle of the clockwise rotation that should be applied to a video frame before displaying.
Definition qvideoframe.h:44
void unmap()
Releases the memory mapped by the map() function.
int height() const
Returns the height of a video frame.
uchar * bits(int plane)
Returns a pointer to the start of the frame data buffer for a plane.
bool map(QVideoFrame::MapMode mode)
Maps the contents of a video frame to system (CPU addressable) memory.
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of this video frame.
int width() const
Returns the width of a video frame.
int mappedBytes(int plane) const
Returns the number of bytes occupied by plane plane of the mapped frame data.
QSize size
the size of the widget excluding any window frame
Definition qwidget.h:113
Combined button and popup list for selecting options.
QString vertexShaderFileName(const QVideoFrameFormat &format)
QString fragmentShaderFileName(const QVideoFrameFormat &format, QRhiSwapChain::Format surfaceFormat)
const TextureDescription * textureDescription(QVideoFrameFormat::PixelFormat format)
std::unique_ptr< QVideoFrameTextures > createTextures(QVideoFrame &frame, QRhi *rhi, QRhiResourceUpdateBatch *rub, std::unique_ptr< QVideoFrameTextures > &&oldTextures)
void updateUniformData(QByteArray *dst, const QVideoFrameFormat &format, const QVideoFrame &frame, const QMatrix4x4 &transform, float opacity, float maxNits)
@ black
Definition qnamespace.h:29
@ AA_ForceRasterWidgets
Definition qnamespace.h:442
constexpr Initialization Uninitialized
Definition image.cpp:4
static void * context
#define Q_FUNC_INFO
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLboolean GLboolean GLboolean b
GLboolean r
[2]
GLfloat GLfloat f
GLuint name
GLint GLsizei GLsizei GLenum format
void ** params
GLuint GLenum GLenum transform
GLuint shader
Definition qopenglext.h:665
GLdouble GLdouble t
Definition qopenglext.h:243
static constexpr QSize frameSize(const T &frame)
static constexpr To convert(const std::array< Mapping, N > &mapping, From Mapping::*from, To Mapping::*to, From value, To defaultValue)
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
VideoFrameConvertFunc qConverterForFormat(QVideoFrameFormat::PixelFormat format)
QImage qImageFromVideoFrame(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
static QImage convertCPU(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
static QImage convertJPEG(const QVideoFrame &frame, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
static QShader vfcGetShader(const QString &name)
static QThreadStorage< State > g_state
static QHash< QString, QShader > g_shaderCache
static QRhi * initializeRHI(QRhi *videoFrameRhi)
static bool pixelFormatHasAlpha(QVideoFrameFormat::PixelFormat format)
static bool updateTextures(QRhi *rhi, std::unique_ptr< QRhiBuffer > &uniformBuffer, std::unique_ptr< QRhiSampler > &textureSampler, std::unique_ptr< QRhiShaderResourceBindings > &shaderResourceBindings, std::unique_ptr< QRhiGraphicsPipeline > &graphicsPipeline, std::unique_ptr< QRhiRenderPassDescriptor > &renderPass, QVideoFrame &frame, const std::unique_ptr< QVideoFrameTextures > &videoFrameTextures)
static void rasterTransform(QImage &image, QVideoFrame::RotationAngle rotation, bool mirrorX, bool mirrorY)
static const float g_quad[]
static void imageCleanupHandler(void *info)
QFileInfo info(fileName)
[8]
QByteArray imageData
[15]
QFrame frame
[0]
\inmodule QtGui
Definition qrhi.h:1686
QByteArray data
Definition qrhi.h:1690
std::function< void()> completed
Definition qrhi.h:1687