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
qgstvideobuffer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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 <private/qvideotexturehelper_p.h>
7#include <qpa/qplatformnativeinterface.h>
8#include <qguiapplication.h>
9#include <QtCore/qapplicationstatic.h>
10#include <QtCore/qloggingcategory.h>
11
12#include <gst/video/video.h>
13#include <gst/video/video-frame.h>
14#include <gst/video/gstvideometa.h>
15#include <gst/pbutils/gstpluginsbaseversion.h>
16
17#include <common/qgstutils_p.h>
18
19#if QT_CONFIG(gstreamer_gl)
20# include <QtGui/rhi/qrhi.h>
21# include <QtGui/qopenglcontext.h>
22# include <QtGui/qopenglfunctions.h>
23# include <QtGui/qopengl.h>
24
25# include <gst/gl/gstglconfig.h>
26# include <gst/gl/gstglmemory.h>
27# include <gst/gl/gstglsyncmeta.h>
28
29# if QT_CONFIG(gstreamer_gl_egl)
30# include <EGL/egl.h>
31# include <EGL/eglext.h>
32# endif
33
34# if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
35# include <gst/allocators/gstdmabuf.h>
36# endif
37#endif
38
40
41Q_STATIC_LOGGING_CATEGORY(qLcGstVideoBuffer, "qt.multimedia.gstreamer.videobuffer");
42
43#if QT_CONFIG(gstreamer_gl) && QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
44Qt::HANDLE getEglDisplay() {
45 using namespace Qt::StringLiterals;
46 return qGuiApp
47 ? qGuiApp->platformNativeInterface()->nativeResourceForIntegration("egldisplay"_ba)
48 : nullptr;
49}
50
51Q_APPLICATION_STATIC(Qt::HANDLE, s_eglDisplay, getEglDisplay());
52
53Q_GLOBAL_STATIC(QFunctionPointer, g_eglImageTargetTexture2D,
54 eglGetProcAddress("glEGLImageTargetTexture2DOES"));
55#endif
56
57// keep things building without drm_fourcc.h
58#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) |
59 ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
60#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
61#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */
62#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
63#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
64#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */
65#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
66#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
67#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
68#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
69#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
70#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
71#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
72#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
73#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
74#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */
75#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */
76#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
77#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
78#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */
79#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel */
80#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */
81#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
82#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */
83#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */
84#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */
85
86QGstVideoBuffer::QGstVideoBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo,
87 const QVideoFrameFormat &frameFormat)
94{
95 m_type = m_memoryFormat != QGstCaps::CpuMemory ? QVideoFrame::RhiTextureHandle
96 : QVideoFrame::NoHandle;
97#if QT_CONFIG(gstreamer_gl) && QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
98 m_eglDisplay = *s_eglDisplay();
99 if (m_eglDisplay)
100 m_eglImageTargetTexture2D = *g_eglImageTargetTexture2D();
101#endif
102}
103
104QGstVideoBuffer::~QGstVideoBuffer()
105{
106 Q_ASSERT(m_mode == QVideoFrame::NotMapped);
107}
108
109QAbstractVideoBuffer::MapData QGstVideoBuffer::map(QVideoFrame::MapMode mode)
110{
111 const GstMapFlags flags = GstMapFlags(((mode & QVideoFrame::ReadOnly) ? GST_MAP_READ : 0)
112 | ((mode & QVideoFrame::WriteOnly) ? GST_MAP_WRITE : 0));
113
114 MapData mapData;
115 if (mode == QVideoFrame::NotMapped || m_mode != QVideoFrame::NotMapped)
116 return mapData;
117
118 const GstVideoInfo &gstVideoInfo = m_videoInfo.gstVideoInfo;
119 if (!gstVideoInfo.finfo || gstVideoInfo.finfo->n_planes == 0) { // Encoded
120 if (gst_buffer_map(m_buffer.get(), &m_frame.map[0], flags)) {
121 mapData.planeCount = 1;
122 mapData.bytesPerLine[0] = -1;
123 mapData.dataSize[0] = m_frame.map[0].size;
124 mapData.data[0] = static_cast<uchar *>(m_frame.map[0].data);
125
126 m_mode = mode;
127 }
128 } else if (gst_video_frame_map(&m_frame, &gstVideoInfo, m_buffer.get(), flags)) {
129 mapData.planeCount = GST_VIDEO_FRAME_N_PLANES(&m_frame);
130
131 for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES(&m_frame); ++i) {
132 mapData.bytesPerLine[i] = GST_VIDEO_FRAME_PLANE_STRIDE(&m_frame, i);
133 mapData.data[i] = static_cast<uchar *>(GST_VIDEO_FRAME_PLANE_DATA(&m_frame, i));
134 mapData.dataSize[i] = mapData.bytesPerLine[i]*GST_VIDEO_FRAME_COMP_HEIGHT(&m_frame, i);
135 }
136
137 m_mode = mode;
138 }
139 return mapData;
140}
141
142void QGstVideoBuffer::unmap()
143{
144 if (m_mode != QVideoFrame::NotMapped) {
145 if (!m_videoInfo.gstVideoInfo.finfo || m_videoInfo.gstVideoInfo.finfo->n_planes == 0)
146 gst_buffer_unmap(m_buffer.get(), &m_frame.map[0]);
147 else
148 gst_video_frame_unmap(&m_frame);
149 }
150 m_mode = QVideoFrame::NotMapped;
151}
152
153bool QGstVideoBuffer::isDmaBuf() const
154{
155 return m_memoryFormat == QGstCaps::DMABuf;
156}
157
158#if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
159
160static int
161fourccFromGstVideoFormat(const GstVideoFormat format, int plane, bool singleEGLImage)
162{
163#if G_BYTE_ORDER == G_LITTLE_ENDIAN
164 const gint argb_fourcc = DRM_FORMAT_ARGB8888;
165 const gint rgba_fourcc = DRM_FORMAT_ABGR8888;
166 const gint rgb_fourcc = DRM_FORMAT_BGR888;
167 const gint rg_fourcc = DRM_FORMAT_GR88;
168#else
169 const gint argb_fourcc = DRM_FORMAT_BGRA8888;
170 const gint rgba_fourcc = DRM_FORMAT_RGBA8888;
171 const gint rgb_fourcc = DRM_FORMAT_RGB888;
172 const gint rg_fourcc = DRM_FORMAT_RG88;
173#endif
174
175 switch (format) {
176 case GST_VIDEO_FORMAT_RGB16:
177 case GST_VIDEO_FORMAT_BGR16:
178 return DRM_FORMAT_RGB565;
179
180 case GST_VIDEO_FORMAT_RGB:
181 case GST_VIDEO_FORMAT_BGR:
182 return rgb_fourcc;
183
184 case GST_VIDEO_FORMAT_BGRx:
185 case GST_VIDEO_FORMAT_BGRA:
186 return argb_fourcc;
187
188 case GST_VIDEO_FORMAT_AYUV:
189 if (singleEGLImage) return DRM_FORMAT_AYUV;
190 [[fallthrough]];
191 case GST_VIDEO_FORMAT_RGBx:
192 case GST_VIDEO_FORMAT_RGBA:
193 case GST_VIDEO_FORMAT_ARGB:
194 case GST_VIDEO_FORMAT_xRGB:
195 case GST_VIDEO_FORMAT_ABGR:
196 case GST_VIDEO_FORMAT_xBGR:
197 return rgba_fourcc;
198
199 case GST_VIDEO_FORMAT_GRAY8:
200 return DRM_FORMAT_R8;
201
202 case GST_VIDEO_FORMAT_YUY2:
203 return DRM_FORMAT_YUYV;
204
205 case GST_VIDEO_FORMAT_UYVY:
206 return DRM_FORMAT_UYVY;
207
208 case GST_VIDEO_FORMAT_GRAY16_LE:
209 case GST_VIDEO_FORMAT_GRAY16_BE:
210 if (singleEGLImage) return DRM_FORMAT_R16;
211 return rg_fourcc;
212
213 case GST_VIDEO_FORMAT_NV12:
214 if (singleEGLImage) return DRM_FORMAT_NV12;
215 [[fallthrough]];
216 case GST_VIDEO_FORMAT_NV21:
217 if (singleEGLImage) return DRM_FORMAT_NV21;
218 return plane == 0 ? DRM_FORMAT_R8 : rg_fourcc;
219
220 case GST_VIDEO_FORMAT_I420:
221 if (singleEGLImage) return DRM_FORMAT_YUV420;
222 [[fallthrough]];
223 case GST_VIDEO_FORMAT_YV12:
224 if (singleEGLImage) return DRM_FORMAT_YVU420;
225 [[fallthrough]];
226 case GST_VIDEO_FORMAT_Y41B:
227 if (singleEGLImage) return DRM_FORMAT_YUV411;
228 [[fallthrough]];
229 case GST_VIDEO_FORMAT_Y42B:
230 if (singleEGLImage) return DRM_FORMAT_YUV422;
231 [[fallthrough]];
232 case GST_VIDEO_FORMAT_Y444:
233 if (singleEGLImage) return DRM_FORMAT_YUV444;
234 return DRM_FORMAT_R8;
235
236 case GST_VIDEO_FORMAT_BGR10A2_LE:
237 return DRM_FORMAT_BGRA1010102;
238
239 case GST_VIDEO_FORMAT_P010_10LE:
240 case GST_VIDEO_FORMAT_P010_10BE:
241 if (singleEGLImage) return DRM_FORMAT_P010;
242 return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_RG1616;
243
244 default:
245 return -1;
246 }
247}
248
249static void logGlAndEglErrors(const char *context)
250{
251 if (!qLcGstVideoBuffer().isDebugEnabled())
252 return;
253
254 const GLenum glError = glGetError();
255 const EGLint eglError = eglGetError();
256 if (glError == GL_NO_ERROR && eglError == EGL_SUCCESS)
257 return;
258
259 qCDebug(qLcGstVideoBuffer).nospace()
260 << context << ": GL error 0x" << Qt::hex << glError
261 << ", EGL error 0x" << eglError;
262}
263#endif
264
265#if QT_CONFIG(gstreamer_gl)
266struct GlTextures
267{
268 uint count = 0;
269 bool owned = false;
270 std::array<guint32, QVideoTextureHelper::TextureDescription::maxPlanes> names{};
271};
272
273class QGstQVideoFrameTextures : public QVideoFrameTextures
274{
275public:
276 QGstQVideoFrameTextures(QRhi *rhi,
277 QSize size,
278 QVideoFrameFormat::PixelFormat format,
279 GlTextures &textures,
280 QGstCaps::MemoryFormat memoryFormat)
281 : m_rhi(rhi)
282 , m_glTextures(textures)
283 {
284 QRhiTexture::Flags textureFlags = {};
285 if (QVideoTextureHelper::forceGlTextureExternalOesIsSet()
286 && m_rhi && rhi->backend() == QRhi::OpenGLES2)
287 textureFlags = {QRhiTexture::ExternalOES};
288
289 bool isDmaBuf = memoryFormat == QGstCaps::DMABuf;
290 auto fallbackPolicy = isDmaBuf
291 ? QVideoTextureHelper::TextureDescription::FallbackPolicy::Disable
292 : QVideoTextureHelper::TextureDescription::FallbackPolicy::Enable;
293
294 auto desc = QVideoTextureHelper::textureDescription(format);
295 for (uint i = 0; i < textures.count; ++i) {
296 // Pass nullptr to rhiPlaneSize to disable fallback in its call to rhiTextureFormat
297 QSize planeSize = desc->rhiPlaneSize(size, i, isDmaBuf ? nullptr : m_rhi);
298 QRhiTexture::Format format = desc->rhiTextureFormat(i, m_rhi, fallbackPolicy);
299 m_textures[i].reset(rhi->newTexture(format, planeSize, 1, textureFlags));
300 m_textures[i]->createFrom({textures.names[i], 0});
301 }
302 }
303
304 ~QGstQVideoFrameTextures() override
305 {
306 m_rhi->makeThreadLocalNativeContextCurrent();
307 auto ctx = QOpenGLContext::currentContext();
308 if (m_glTextures.owned && ctx)
309 ctx->functions()->glDeleteTextures(int(m_glTextures.count), m_glTextures.names.data());
310 }
311
312 QRhiTexture *texture(uint plane) const override
313 {
314 return plane < m_glTextures.count ? m_textures[plane].get() : nullptr;
315 }
316
317private:
318 QRhi *m_rhi = nullptr;
319 GlTextures m_glTextures;
320 std::unique_ptr<QRhiTexture> m_textures[QVideoTextureHelper::TextureDescription::maxPlanes];
321};
322
323static GlTextures mapFromGlTexture(const QGstBufferHandle &bufferHandle, GstVideoFrame &frame,
324 GstVideoInfo &videoInfo)
325{
326 qCDebug(qLcGstVideoBuffer) << "Mapping textures from GL memory";
327
328 GstBuffer *buffer = bufferHandle.get();
329 auto *mem = GST_GL_BASE_MEMORY_CAST(gst_buffer_peek_memory(buffer, 0));
330 if (!mem)
331 return {};
332
333 if (!gst_video_frame_map(&frame, &videoInfo, buffer, GstMapFlags(GST_MAP_READ|GST_MAP_GL))) {
334 qWarning() << "Could not map GL textures";
335 return {};
336 }
337
338 auto *sync_meta = gst_buffer_get_gl_sync_meta(buffer);
339 GstBuffer *sync_buffer = nullptr;
340 if (!sync_meta) {
341 sync_buffer = gst_buffer_new();
342 sync_meta = gst_buffer_add_gl_sync_meta(mem->context, sync_buffer);
343 }
344 gst_gl_sync_meta_set_sync_point (sync_meta, mem->context);
345 gst_gl_sync_meta_wait (sync_meta, mem->context);
346 if (sync_buffer)
347 gst_buffer_unref(sync_buffer);
348
349 GlTextures textures;
350 textures.count = frame.info.finfo->n_planes;
351
352 for (uint i = 0; i < textures.count; ++i)
353 textures.names[i] = *(guint32 *)frame.data[i];
354
355 gst_video_frame_unmap(&frame);
356
357 return textures;
358}
359
360# if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
361static GlTextures mapFromDmaBuffer(QRhi *rhi, const QGstBufferHandle &bufferHandle,
362 const QGstVideoInfo &videoInfo, Qt::HANDLE eglDisplay,
363 QFunctionPointer eglImageTargetTexture2D)
364{
365 qCDebug(qLcGstVideoBuffer) << "Importing textures from DMA buffer";
366 logGlAndEglErrors("mapFromDmaBuffer");
367
368 GstBuffer *buffer = bufferHandle.get();
369
370 Q_ASSERT(gst_is_dmabuf_memory(gst_buffer_peek_memory(buffer, 0)));
371 Q_ASSERT(eglDisplay);
372 Q_ASSERT(eglImageTargetTexture2D);
373 Q_ASSERT(QGuiApplication::platformName() == QLatin1String("eglfs"));
374 Q_ASSERT(rhi);
375
376 auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
377 auto glContext = nativeHandles->context;
378 if (!glContext) {
379 qCWarning(qLcGstVideoBuffer) << "no GL context";
380 return {};
381 }
382
383 const GstVideoInfo &gstVideoInfo = videoInfo.gstVideoInfo;
384 if (!gstVideoInfo.finfo) {
385 qCWarning(qLcGstVideoBuffer) << "Missing valid GstVideoInfo for DMABuf GstBuffer";
386 return {};
387 }
388
389 if (videoInfo.dmaDrmModifier && *videoInfo.dmaDrmModifier != 0) {
390 qCWarning(qLcGstVideoBuffer) << "Unsupported non-linear DMABuf modifier:"
391 << Qt::hex << *videoInfo.dmaDrmModifier;
392 return {};
393 }
394
395 const GstVideoMeta *videoMeta = gst_buffer_get_video_meta(buffer);
396 const GstVideoFormat videoInfoFormat = GST_VIDEO_INFO_FORMAT(&gstVideoInfo);
397 GstVideoFormat format = videoMeta ? videoMeta->format : videoInfoFormat;
398 if (format == GST_VIDEO_FORMAT_UNKNOWN)
399 format = videoInfoFormat;
400
401 const int nPlanes = videoMeta ? videoMeta->n_planes : GST_VIDEO_INFO_N_PLANES(&gstVideoInfo);
402 const int nMemoryBlocks = gst_buffer_n_memory(buffer);
403 static const bool externalOes = QVideoTextureHelper::forceGlTextureExternalOesIsSet();
404 static const bool singleEGLImage =
405 externalOes || qEnvironmentVariableIsSet("QT_GSTREAMER_FORCE_SINGLE_EGLIMAGE");
406
407 qCDebug(qLcGstVideoBuffer) << "format:" << gst_video_format_to_string(format)
408 << "nPlanes:" << nPlanes
409 << "nMemoryBlocks:" << nMemoryBlocks
410 << "externalOes:" << externalOes
411 << "singleEGLImage:" << singleEGLImage;
412
413 constexpr int maxPlanes = 4;
414 Q_ASSERT(nPlanes >= 1
415 && nPlanes <= maxPlanes
416 && (nMemoryBlocks == 1 || nMemoryBlocks == nPlanes));
417
418 const int nEGLImages = singleEGLImage ? 1 : nPlanes;
419 std::array<EGLAttrib, maxPlanes> planeFourcc{};
420 for (int plane = 0; plane < nEGLImages; ++plane) {
421 const int fourcc = fourccFromGstVideoFormat(format, plane, singleEGLImage);
422 if (fourcc < 0) {
423 qCWarning(qLcGstVideoBuffer) << "Unsupported format for DMABuf:"
424 << gst_video_format_to_string(format) << "plane:" << plane
425 << "singleEGLImage" << singleEGLImage;
426 return {};
427 }
428 planeFourcc[plane] = EGLAttrib(fourcc);
429 }
430
431 GlTextures textures = {};
432 textures.owned = true;
433 textures.count = nEGLImages;
434
435 QOpenGLFunctions functions(glContext);
436 functions.glGenTextures(int(textures.count), textures.names.data());
437 logGlAndEglErrors("glGenTextures");
438
439 std::array<int, maxPlanes> fds{-1, -1, -1, -1};
440 for (int i = 0; i < nMemoryBlocks && i < maxPlanes; ++i) {
441 fds[i] = gst_dmabuf_memory_get_fd(gst_buffer_peek_memory(buffer, i));
442 }
443
444 auto fdForPlane = [&](int plane) -> EGLAttrib {
445 if (plane < 0 || plane >= maxPlanes || plane >= nMemoryBlocks)
446 return fds[0];
447 return (fds[plane] >= 0) ? fds[plane] : fds[0];
448 };
449
450 auto compWidth = [&](int plane) -> EGLAttrib {
451 return singleEGLImage ? GST_VIDEO_INFO_WIDTH(&gstVideoInfo)
452 : GST_VIDEO_INFO_COMP_WIDTH(&gstVideoInfo, plane);
453 };
454
455 auto compHeight = [&](int plane) -> EGLAttrib {
456 return singleEGLImage ? GST_VIDEO_INFO_HEIGHT(&gstVideoInfo)
457 : GST_VIDEO_INFO_COMP_HEIGHT(&gstVideoInfo, plane);
458 };
459
460 auto planeOffset = [&](int plane) -> EGLAttrib {
461 // videoMeta/videoInfo offset can be incorrect when each plane has a separate memory black.
462 if (nPlanes == nMemoryBlocks)
463 return 0;
464 if (videoMeta)
465 return videoMeta->offset[plane];
466 return GST_VIDEO_INFO_PLANE_OFFSET(&gstVideoInfo, plane);
467 };
468
469 auto planeStride = [&](int plane) -> EGLAttrib {
470 if (videoMeta)
471 return videoMeta->stride[plane];
472 return GST_VIDEO_INFO_PLANE_STRIDE(&gstVideoInfo, plane);
473 };
474
475 for (int plane = 0; plane < nEGLImages; ++plane) {
476 constexpr int maxAttrCount = 31;
477 std::array<EGLAttrib, maxAttrCount> attr;
478 int i = 0;
479
480 const int width = compWidth(plane);
481 const int height = compHeight(plane);
482
483 attr[i++] = EGL_WIDTH;
484 attr[i++] = width;
485 attr[i++] = EGL_HEIGHT;
486 attr[i++] = height;
487 attr[i++] = EGL_LINUX_DRM_FOURCC_EXT;
488 attr[i++] = planeFourcc[plane];
489
490 attr[i++] = EGL_DMA_BUF_PLANE0_FD_EXT;
491 attr[i++] = fdForPlane(plane);
492 attr[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
493 attr[i++] = planeOffset(plane);
494 attr[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
495 attr[i++] = planeStride(plane);
496
497 if (singleEGLImage && nPlanes > 1) {
498 attr[i++] = EGL_DMA_BUF_PLANE1_FD_EXT;
499 attr[i++] = fdForPlane(1);
500 attr[i++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
501 attr[i++] = planeOffset(1);
502 attr[i++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
503 attr[i++] = planeStride(1);
504 }
505
506 if (singleEGLImage && nPlanes > 2) {
507 attr[i++] = EGL_DMA_BUF_PLANE2_FD_EXT;
508 attr[i++] = fdForPlane(2);
509 attr[i++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
510 attr[i++] = planeOffset(2);
511 attr[i++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
512 attr[i++] = planeStride(2);
513 }
514
515 if (singleEGLImage && nPlanes > 3) {
516 attr[i++] = EGL_DMA_BUF_PLANE3_FD_EXT;
517 attr[i++] = fdForPlane(3);
518 attr[i++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
519 attr[i++] = planeOffset(3);
520 attr[i++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
521 attr[i++] = planeStride(3);
522 }
523
524 attr[i++] = EGL_NONE;
525 Q_ASSERT(i <= maxAttrCount);
526
527 EGLImage image = eglCreateImage(eglDisplay,
528 EGL_NO_CONTEXT,
529 EGL_LINUX_DMA_BUF_EXT,
530 nullptr,
531 attr.data());
532 if (image == EGL_NO_IMAGE_KHR) {
533 qCWarning(qLcGstVideoBuffer) << "could not create EGL image for plane" << plane
534 << ", EGL error 0x" << Qt::hex << eglGetError();
535 continue;
536 }
537 logGlAndEglErrors("eglCreateImage");
538
539 #ifdef GL_OES_EGL_image_external
540 GLenum target = externalOes ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
541 #else
542 GLenum target = GL_TEXTURE_2D;
543 #endif
544 functions.glBindTexture(target, textures.names[plane]);
545
546 auto EGLImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglImageTargetTexture2D;
547 EGLImageTargetTexture2D(target, image);
548 logGlAndEglErrors("glEGLImageTargetTexture2DOES");
549
550 eglDestroyImage(eglDisplay, image);
551 }
552
553 return textures;
554}
555#endif
556#endif
557
558QVideoFrameTexturesUPtr QGstVideoBuffer::mapTextures(QRhi &rhi, QVideoFrameTexturesUPtr& /*oldTextures*/)
559{
560#if QT_CONFIG(gstreamer_gl)
561# if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
562 static const bool isEglfsQPA = QGuiApplication::platformName() == QLatin1String("eglfs");
563# endif
564 GlTextures textures = {};
565 if (m_memoryFormat == QGstCaps::GLTexture)
566 textures = mapFromGlTexture(m_buffer, m_frame, m_videoInfo.gstVideoInfo);
567
568# if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(linux_dmabuf)
569 else if (m_memoryFormat == QGstCaps::DMABuf && m_eglDisplay && isEglfsQPA)
570 textures = mapFromDmaBuffer(&rhi, m_buffer, m_videoInfo, m_eglDisplay,
571 m_eglImageTargetTexture2D);
572
573# endif
574 if (textures.count > 0)
575 return std::make_unique<QGstQVideoFrameTextures>(
576 &rhi, QSize{ m_videoInfo.gstVideoInfo.width, m_videoInfo.gstVideoInfo.height },
577 m_frameFormat.pixelFormat(), textures, m_memoryFormat);
578#endif
579 return {};
580}
581
582QT_END_NAMESPACE
@ DMABuf
Definition qgst_p.h:358
QGstVideoBuffer(QGstBufferHandle buffer, const QGstVideoInfo &videoInfo, const QVideoFrameFormat &frameFormat)
~QGstVideoBuffer() override
bool isDmaBuf() const override
QVideoFrameTexturesUPtr mapTextures(QRhi &, QVideoFrameTexturesUPtr &) override
void unmap() override
Releases the memory mapped by the map() function.
MapData map(QVideoFrame::MapMode mode) override
Maps the planes of a video buffer to memory.
Combined button and popup list for selecting options.
#define fourcc_code(a, b, c, d)