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
qffmpeghwaccel_vaapi.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
6#if !QT_CONFIG(vaapi)
7#error "Configuration error"
8#endif
9
10#include <va/va.h>
11
12#include <qvideoframeformat.h>
14#include "private/qvideotexturehelper_p.h"
15
16#include <rhi/qrhi.h>
17
18#include <qguiapplication.h>
19#include <qpa/qplatformnativeinterface.h>
20
21#include <qopenglfunctions.h>
22
23//#define VA_EXPORT_USE_LAYERS
24
25#if __has_include("drm/drm_fourcc.h")
26#include <drm/drm_fourcc.h>
27#elif __has_include("libdrm/drm_fourcc.h")
28#include <libdrm/drm_fourcc.h>
29#else
30// keep things building without drm_fourcc.h
31#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
32 ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
33
34#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
35#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
36#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
37#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
38#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
39#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
40#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
41#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
42#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
43#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
44#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
45#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
46#endif
47
48extern "C" {
49#include <libavutil/hwcontext_vaapi.h>
50}
51
52#include <va/va_drm.h>
53#include <va/va_drmcommon.h>
54
55#include <EGL/egl.h>
56#include <EGL/eglext.h>
57
58#include <unistd.h>
59
60#include <qloggingcategory.h>
61
63
64static Q_LOGGING_CATEGORY(qLHWAccelVAAPI, "qt.multimedia.ffmpeg.hwaccelvaapi");
65
66namespace QFFmpeg {
67
69{
70#if G_BYTE_ORDER == G_LITTLE_ENDIAN
71 const quint32 rgba_fourcc = DRM_FORMAT_ABGR8888;
72 const quint32 rg_fourcc = DRM_FORMAT_GR88;
73 const quint32 rg16_fourcc = DRM_FORMAT_GR1616;
74#else
75 const quint32 rgba_fourcc = DRM_FORMAT_RGBA8888;
76 const quint32 rg_fourcc = DRM_FORMAT_RG88;
77 const quint32 rg16_fourcc = DRM_FORMAT_RG1616;
78#endif
79
80// qCDebug(qLHWAccelVAAPI) << "Getting DRM fourcc for pixel format" << format;
81
82 switch (format) {
91 return nullptr;
92
107 {
108 static constexpr quint32 format[] = { rgba_fourcc, 0, 0, 0 };
109 return format;
110 }
111
113 {
114 static constexpr quint32 format[] = { DRM_FORMAT_R8, 0, 0, 0 };
115 return format;
116 }
118 {
119 static constexpr quint32 format[] = { DRM_FORMAT_R16, 0, 0, 0 };
120 return format;
121 }
122
126 {
127 static constexpr quint32 format[] = { DRM_FORMAT_R8, DRM_FORMAT_R8, DRM_FORMAT_R8, 0 };
128 return format;
129 }
131 {
132 static constexpr quint32 format[] = { DRM_FORMAT_R16, DRM_FORMAT_R16, DRM_FORMAT_R16, 0 };
133 return format;
134 }
135
138 {
139 static constexpr quint32 format[] = { DRM_FORMAT_R8, rg_fourcc, 0, 0 };
140 return format;
141 }
142
145 {
146 static constexpr quint32 format[] = { DRM_FORMAT_R16, rg16_fourcc, 0, 0 };
147 return format;
148 }
149 }
150 return nullptr;
151}
152
154{
155public:
157 qint64 textureHandle(QRhi *, int plane) override {
158 return textures[plane];
159 }
160
161 QRhi *rhi = nullptr;
163 int nPlanes = 0;
165};
166
167
168VAAPITextureConverter::VAAPITextureConverter(QRhi *rhi)
169 : TextureConverterBackend(nullptr)
170{
171 qCDebug(qLHWAccelVAAPI) << ">>>> Creating VAAPI HW accelerator";
172
173 if (!rhi || rhi->backend() != QRhi::OpenGLES2) {
174 qWarning() << "VAAPITextureConverter: No rhi or non openGL based RHI";
175 this->rhi = nullptr;
176 return;
177 }
178
179 auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
180 glContext = nativeHandles->context;
181 if (!glContext) {
182 qCDebug(qLHWAccelVAAPI) << " no GL context, disabling";
183 return;
184 }
187 eglDisplay = pni->nativeResourceForIntegration("egldisplay");
188 qCDebug(qLHWAccelVAAPI) << " platform is" << platform << eglDisplay;
189
190 if (!eglDisplay) {
191 qCDebug(qLHWAccelVAAPI) << " no egl display, disabling";
192 return;
193 }
194 eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
195 if (!eglDisplay) {
196 qCDebug(qLHWAccelVAAPI) << " no eglImageTargetTexture2D, disabling";
197 return;
198 }
199
200 // everything ok, indicate that we can do zero copy
201 this->rhi = rhi;
202}
203
204VAAPITextureConverter::~VAAPITextureConverter()
205{
206}
207
208//#define VA_EXPORT_USE_LAYERS
209TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame)
210{
211// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel::getTextures";
212 if (frame->format != AV_PIX_FMT_VAAPI || !eglDisplay) {
213 qCDebug(qLHWAccelVAAPI) << "format/egl error" << frame->format << eglDisplay;
214 return nullptr;
215 }
216
217 if (!frame->hw_frames_ctx)
218 return nullptr;
219
220 auto *fCtx = (AVHWFramesContext *)frame->hw_frames_ctx->data;
221 auto *ctx = fCtx->device_ctx;
222 if (!ctx)
223 return nullptr;
224
225 auto *vaCtx = (AVVAAPIDeviceContext *)ctx->hwctx;
226 auto vaDisplay = vaCtx->display;
227 if (!vaDisplay) {
228 qCDebug(qLHWAccelVAAPI) << " no VADisplay, disabling";
229 return nullptr;
230 }
231
232 VASurfaceID vaSurface = (uintptr_t)frame->data[3];
233
234 VADRMPRIMESurfaceDescriptor prime = {};
235 if (vaExportSurfaceHandle(vaDisplay, vaSurface,
236 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
237 VA_EXPORT_SURFACE_READ_ONLY |
238#ifdef VA_EXPORT_USE_LAYERS
239 VA_EXPORT_SURFACE_SEPARATE_LAYERS,
240#else
241 VA_EXPORT_SURFACE_COMPOSED_LAYERS,
242#endif
243 &prime) != VA_STATUS_SUCCESS)
244 {
245 qWarning() << "vaExportSurfaceHandle failed";
246 return nullptr;
247 }
248
249 // Make sure all fd's in 'prime' are closed when we return from this function
250 QScopeGuard closeObjectsGuard([&prime]() {
251 for (uint32_t i = 0; i < prime.num_objects; ++i)
252 close(prime.objects[i].fd);
253 });
254
255 // ### Check that prime.fourcc is what we expect
256 vaSyncSurface(vaDisplay, vaSurface);
257
258// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: vaSufraceDesc: width/height" << prime.width << prime.height << "num objects"
259// << prime.num_objects << "num layers" << prime.num_layers;
260
261 QOpenGLFunctions functions(glContext);
262
263 AVPixelFormat fmt = HWAccel::format(frame);
264 bool needsConversion;
265 auto qtFormat = QFFmpegVideoBuffer::toQtPixelFormat(fmt, &needsConversion);
266 auto *drm_formats = fourccFromPixelFormat(qtFormat);
267 if (!drm_formats || needsConversion) {
268 qWarning() << "can't use DMA transfer for pixel format" << fmt << qtFormat;
269 return nullptr;
270 }
271
273 int nPlanes = 0;
274 for (; nPlanes < 5; ++nPlanes) {
275 if (drm_formats[nPlanes] == 0)
276 break;
277 }
278 Q_ASSERT(nPlanes == desc->nplanes);
279 nPlanes = desc->nplanes;
280// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: nPlanes" << nPlanes;
281
283
284 EGLImage images[4];
285 GLuint glTextures[4] = {};
286 functions.glGenTextures(nPlanes, glTextures);
287 for (int i = 0; i < nPlanes; ++i) {
288#ifdef VA_EXPORT_USE_LAYERS
289#define LAYER i
290#define PLANE 0
291 if (prime.layers[i].drm_format != drm_formats[i]) {
292 qWarning() << "expected DRM format check failed expected"
293 << Qt::hex << drm_formats[i] << "got" << prime.layers[i].drm_format;
294 }
295#else
296#define LAYER 0
297#define PLANE i
298#endif
299
300 EGLAttrib img_attr[] = {
301 EGL_LINUX_DRM_FOURCC_EXT, (EGLint)drm_formats[i],
302 EGL_WIDTH, desc->widthForPlane(frame->width, i),
303 EGL_HEIGHT, desc->heightForPlane(frame->height, i),
304 EGL_DMA_BUF_PLANE0_FD_EXT, prime.objects[prime.layers[LAYER].object_index[PLANE]].fd,
305 EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLint)prime.layers[LAYER].offset[PLANE],
306 EGL_DMA_BUF_PLANE0_PITCH_EXT, (EGLint)prime.layers[LAYER].pitch[PLANE],
307 EGL_NONE
308 };
309 images[i] = eglCreateImage(eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr);
310 if (!images[i]) {
311 const GLenum error = eglGetError();
312 if (error == EGL_BAD_MATCH) {
313 qWarning() << "eglCreateImage failed for plane" << i << "with error code EGL_BAD_MATCH, "
314 "disabling hardware acceleration. This could indicate an EGL implementation issue."
315 "\nVAAPI driver: " << vaQueryVendorString(vaDisplay)
316 << "\nEGL vendor:" << eglQueryString(eglDisplay, EGL_VENDOR);
317 this->rhi = nullptr; // Disabling texture conversion here to fix QTBUG-112312
318 return nullptr;
319 }
320 if (error) {
321 qWarning() << "eglCreateImage failed for plane" << i << "with error code" << error;
322 return nullptr;
323 }
324 }
325 functions.glActiveTexture(GL_TEXTURE0 + i);
326 functions.glBindTexture(GL_TEXTURE_2D, glTextures[i]);
327
328 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC eglImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)this->eglImageTargetTexture2D;
329 eglImageTargetTexture2D(GL_TEXTURE_2D, images[i]);
330 GLenum error = glGetError();
331 if (error)
332 qWarning() << "eglImageTargetTexture2D failed with error code" << error;
333 }
334
335 for (int i = 0; i < nPlanes; ++i) {
336 functions.glActiveTexture(GL_TEXTURE0 + i);
337 functions.glBindTexture(GL_TEXTURE_2D, 0);
338 eglDestroyImage(eglDisplay, images[i]);
339 }
340
341 VAAPITextureSet *textureSet = new VAAPITextureSet;
342 textureSet->nPlanes = nPlanes;
343 textureSet->rhi = rhi;
344 textureSet->glContext = glContext;
345
346 for (int i = 0; i < 4; ++i)
347 textureSet->textures[i] = glTextures[i];
348// qCDebug(qLHWAccelVAAPI) << "VAAPIAccel: got textures" << textures[0] << textures[1] << textures[2] << textures[3];
349
350 return textureSet;
351}
352
361
362}
363
static QVideoFrameFormat::PixelFormat toQtPixelFormat(AVPixelFormat avPixelFormat, bool *needsConversion=nullptr)
static AVPixelFormat format(AVFrame *frame)
qint64 textureHandle(QRhi *, int plane) override
static QPlatformNativeInterface * platformNativeInterface()
QString platformName
The name of the underlying platform plugin.
\inmodule QtGui
The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API.
void glDeleteTextures(GLsizei n, const GLuint *textures)
Convenience function that calls glDeleteTextures(n, textures).
The QPlatformNativeInterface class provides an abstraction for retrieving native resource handles.
\variable QRhiGles2InitParams::format
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
bool makeThreadLocalNativeContextCurrent()
With OpenGL this makes the OpenGL context current on the current thread.
Definition qrhi.cpp:10158
Implementation backend() const
Definition qrhi.cpp:8651
@ OpenGLES2
Definition qrhi.h:1809
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10137
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
PixelFormat
Enumerates video data types.
int width
the width of the widget excluding any window frame
Definition qwidget.h:114
int height
the height of the widget excluding any window frame
Definition qwidget.h:115
EGLContext ctx
static const quint32 * fourccFromPixelFormat(const QVideoFrameFormat::PixelFormat format)
Combined button and popup list for selecting options.
const TextureDescription * textureDescription(QVideoFrameFormat::PixelFormat format)
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
DBusConnection const char DBusError * error
intptr_t EGLAttrib
#define DRM_FORMAT_R16
#define DRM_FORMAT_R8
#define DRM_FORMAT_ABGR8888
#define DRM_FORMAT_GR88
#define DRM_FORMAT_RG88
#define PLANE
#define DRM_FORMAT_GR1616
#define DRM_FORMAT_RGBA8888
#define LAYER
#define DRM_FORMAT_RG1616
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLuint const GLuint GLuint const GLuint * textures
typedef GLenum(GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void)
GLint GLsizei GLsizei GLenum format
#define GL_TEXTURE0
Definition qopenglext.h:129
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define GLuint
unsigned int quint32
Definition qtypes.h:50
long long qint64
Definition qtypes.h:60
QVideoFrameFormat::PixelFormat fmt
QT_BEGIN_NAMESPACE Platform platform()
QList< QImage > images
[6]
QObject::connect nullptr
QFrame frame
[0]