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
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
4#include "qgstvideobuffer_p.h"
6#include <private/qvideotexturehelper_p.h>
7#include <qpa/qplatformnativeinterface.h>
8#include <qguiapplication.h>
9
10#include <gst/video/video.h>
11#include <gst/video/video-frame.h>
12#include <gst/video/gstvideometa.h>
13#include <gst/pbutils/gstpluginsbaseversion.h>
14
15#include <common/qgstutils_p.h>
16
17#if QT_CONFIG(gstreamer_gl)
18# include <QtGui/rhi/qrhi.h>
19# include <QtGui/qopenglcontext.h>
20# include <QtGui/qopenglfunctions.h>
21# include <QtGui/qopengl.h>
22
23# include <gst/gl/gstglconfig.h>
24# include <gst/gl/gstglmemory.h>
25# include <gst/gl/gstglsyncmeta.h>
26
27# include <EGL/egl.h>
28# include <EGL/eglext.h>
29
30# if QT_CONFIG(linux_dmabuf)
31# include <gst/allocators/gstdmabuf.h>
32# endif
33#endif
34
36
37// keep things building without drm_fourcc.h
38#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
39 ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
40
41#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
42#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
43#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
44#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
45#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
46#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
47#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
48#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
49#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
50#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
51#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
52#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
53
55 QGstreamerVideoSink *sink, const QVideoFrameFormat &frameFormat,
57 : QAbstractVideoBuffer((sink && sink->rhi() && format != QGstCaps::CpuMemory)
58 ? QVideoFrame::RhiTextureHandle
59 : QVideoFrame::NoHandle,
60 sink ? sink->rhi() : nullptr),
61 memoryFormat(format),
62 m_frameFormat(frameFormat),
63 m_rhi(sink ? sink->rhi() : nullptr),
64 m_videoInfo(info),
65 m_buffer(std::move(buffer))
66{
67 if (sink) {
68 eglDisplay = sink->eglDisplay();
69 eglImageTargetTexture2D = sink->eglImageTargetTexture2D();
70 }
71
72#if !QT_CONFIG(gstreamer_gl)
73 Q_UNUSED(memoryFormat);
74#endif
75}
76
81
82
84{
85 return m_mode;
86}
87
89{
90 const GstMapFlags flags = GstMapFlags(((mode & QVideoFrame::ReadOnly) ? GST_MAP_READ : 0)
91 | ((mode & QVideoFrame::WriteOnly) ? GST_MAP_WRITE : 0));
92
95 return mapData;
96
97 if (m_videoInfo.finfo->n_planes == 0) { // Encoded
98 if (gst_buffer_map(m_buffer.get(), &m_frame.map[0], flags)) {
99 mapData.nPlanes = 1;
100 mapData.bytesPerLine[0] = -1;
101 mapData.size[0] = m_frame.map[0].size;
102 mapData.data[0] = static_cast<uchar *>(m_frame.map[0].data);
103
104 m_mode = mode;
105 }
106 } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer.get(), flags)) {
107 mapData.nPlanes = GST_VIDEO_FRAME_N_PLANES(&m_frame);
108
109 for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES(&m_frame); ++i) {
110 mapData.bytesPerLine[i] = GST_VIDEO_FRAME_PLANE_STRIDE(&m_frame, i);
111 mapData.data[i] = static_cast<uchar *>(GST_VIDEO_FRAME_PLANE_DATA(&m_frame, i));
112 mapData.size[i] = mapData.bytesPerLine[i]*GST_VIDEO_FRAME_COMP_HEIGHT(&m_frame, i);
113 }
114
115 m_mode = mode;
116 }
117 return mapData;
118}
119
121{
122 if (m_mode != QVideoFrame::NotMapped) {
123 if (m_videoInfo.finfo->n_planes == 0)
124 gst_buffer_unmap(m_buffer.get(), &m_frame.map[0]);
125 else
126 gst_video_frame_unmap(&m_frame);
127 }
128 m_mode = QVideoFrame::NotMapped;
129}
130
131#if QT_CONFIG(gstreamer_gl) && QT_CONFIG(linux_dmabuf)
132static int
133fourccFromVideoInfo(const GstVideoInfo * info, int plane)
134{
135 GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info);
136#if G_BYTE_ORDER == G_LITTLE_ENDIAN
137 const gint rgba_fourcc = DRM_FORMAT_ABGR8888;
138 const gint rgb_fourcc = DRM_FORMAT_BGR888;
139 const gint rg_fourcc = DRM_FORMAT_GR88;
140#else
141 const gint rgba_fourcc = DRM_FORMAT_RGBA8888;
142 const gint rgb_fourcc = DRM_FORMAT_RGB888;
143 const gint rg_fourcc = DRM_FORMAT_RG88;
144#endif
145
146 GST_DEBUG ("Getting DRM fourcc for %s plane %i",
147 gst_video_format_to_string (format), plane);
148
149 switch (format) {
150 case GST_VIDEO_FORMAT_RGB16:
151 case GST_VIDEO_FORMAT_BGR16:
152 return DRM_FORMAT_RGB565;
153
154 case GST_VIDEO_FORMAT_RGB:
155 case GST_VIDEO_FORMAT_BGR:
156 return rgb_fourcc;
157
158 case GST_VIDEO_FORMAT_RGBA:
159 case GST_VIDEO_FORMAT_RGBx:
160 case GST_VIDEO_FORMAT_BGRA:
161 case GST_VIDEO_FORMAT_BGRx:
162 case GST_VIDEO_FORMAT_ARGB:
163 case GST_VIDEO_FORMAT_xRGB:
164 case GST_VIDEO_FORMAT_ABGR:
165 case GST_VIDEO_FORMAT_xBGR:
166 case GST_VIDEO_FORMAT_AYUV:
167#if GST_CHECK_PLUGINS_BASE_VERSION(1,16,0)
168 case GST_VIDEO_FORMAT_VUYA:
169#endif
170 return rgba_fourcc;
171
172 case GST_VIDEO_FORMAT_GRAY8:
173 return DRM_FORMAT_R8;
174
175 case GST_VIDEO_FORMAT_YUY2:
176 case GST_VIDEO_FORMAT_UYVY:
177 case GST_VIDEO_FORMAT_GRAY16_LE:
178 case GST_VIDEO_FORMAT_GRAY16_BE:
179 return rg_fourcc;
180
181 case GST_VIDEO_FORMAT_NV12:
182 case GST_VIDEO_FORMAT_NV21:
183 return plane == 0 ? DRM_FORMAT_R8 : rg_fourcc;
184
185 case GST_VIDEO_FORMAT_I420:
186 case GST_VIDEO_FORMAT_YV12:
187 case GST_VIDEO_FORMAT_Y41B:
188 case GST_VIDEO_FORMAT_Y42B:
189 case GST_VIDEO_FORMAT_Y444:
190 return DRM_FORMAT_R8;
191
192#if GST_CHECK_PLUGINS_BASE_VERSION(1,16,0)
193 case GST_VIDEO_FORMAT_BGR10A2_LE:
195#endif
196
197// case GST_VIDEO_FORMAT_RGB10A2_LE:
198// return DRM_FORMAT_RGBA1010102;
199
200 case GST_VIDEO_FORMAT_P010_10LE:
201// case GST_VIDEO_FORMAT_P012_LE:
202// case GST_VIDEO_FORMAT_P016_LE:
203 return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_GR1616;
204
205 case GST_VIDEO_FORMAT_P010_10BE:
206// case GST_VIDEO_FORMAT_P012_BE:
207// case GST_VIDEO_FORMAT_P016_BE:
208 return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_RG1616;
209
210 default:
211 GST_ERROR ("Unsupported format for DMABuf.");
212 return -1;
213 }
214}
215#endif
216
217#if QT_CONFIG(gstreamer_gl)
218struct GlTextures
219{
220 uint count = 0;
221 bool owned = false;
222 std::array<guint32, QVideoTextureHelper::TextureDescription::maxPlanes> names{};
223};
224
225class QGstQVideoFrameTextures : public QVideoFrameTextures
226{
227public:
228 QGstQVideoFrameTextures(QRhi *rhi, QSize size, QVideoFrameFormat::PixelFormat format, GlTextures &textures)
229 : m_rhi(rhi)
230 , m_glTextures(textures)
231 {
233 for (uint i = 0; i < textures.count; ++i) {
234 QSize planeSize(desc->widthForPlane(size.width(), int(i)),
235 desc->heightForPlane(size.height(), int(i)));
236 m_textures[i].reset(rhi->newTexture(desc->textureFormat[i], planeSize, 1, {}));
237 m_textures[i]->createFrom({textures.names[i], 0});
238 }
239 }
240
241 ~QGstQVideoFrameTextures()
242 {
243 m_rhi->makeThreadLocalNativeContextCurrent();
245 if (m_glTextures.owned && ctx)
246 ctx->functions()->glDeleteTextures(int(m_glTextures.count), m_glTextures.names.data());
247 }
248
249 QRhiTexture *texture(uint plane) const override
250 {
251 return plane < m_glTextures.count ? m_textures[plane].get() : nullptr;
252 }
253
254private:
255 QRhi *m_rhi = nullptr;
256 GlTextures m_glTextures;
257 std::unique_ptr<QRhiTexture> m_textures[QVideoTextureHelper::TextureDescription::maxPlanes];
258};
259
260static GlTextures mapFromGlTexture(const QGstBufferHandle &bufferHandle, GstVideoFrame &frame,
261 GstVideoInfo &videoInfo)
262{
263 GstBuffer *buffer = bufferHandle.get();
264 auto *mem = GST_GL_BASE_MEMORY_CAST(gst_buffer_peek_memory(buffer, 0));
265 if (!mem)
266 return {};
267
268 if (!gst_video_frame_map(&frame, &videoInfo, buffer, GstMapFlags(GST_MAP_READ|GST_MAP_GL))) {
269 qWarning() << "Could not map GL textures";
270 return {};
271 }
272
273 auto *sync_meta = gst_buffer_get_gl_sync_meta(buffer);
274 GstBuffer *sync_buffer = nullptr;
275 if (!sync_meta) {
276 sync_buffer = gst_buffer_new();
277 sync_meta = gst_buffer_add_gl_sync_meta(mem->context, sync_buffer);
278 }
279 gst_gl_sync_meta_set_sync_point (sync_meta, mem->context);
280 gst_gl_sync_meta_wait (sync_meta, mem->context);
281 if (sync_buffer)
282 gst_buffer_unref(sync_buffer);
283
284 GlTextures textures;
285 textures.count = frame.info.finfo->n_planes;
286
287 for (uint i = 0; i < textures.count; ++i)
288 textures.names[i] = *(guint32 *)frame.data[i];
289
290 gst_video_frame_unmap(&frame);
291
292 return textures;
293}
294
295#if GST_GL_HAVE_PLATFORM_EGL && QT_CONFIG(linux_dmabuf)
296static GlTextures mapFromDmaBuffer(QRhi *rhi, const QGstBufferHandle &bufferHandle,
297 GstVideoFrame &frame, GstVideoInfo &videoInfo,
298 Qt::HANDLE eglDisplay, QFunctionPointer eglImageTargetTexture2D)
299{
300 GstBuffer *buffer = bufferHandle.get();
301
302 Q_ASSERT(gst_is_dmabuf_memory(gst_buffer_peek_memory(buffer, 0)));
303 Q_ASSERT(eglDisplay);
304 Q_ASSERT(eglImageTargetTexture2D);
305
306 auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
307 auto glContext = nativeHandles->context;
308 if (!glContext) {
309 qWarning() << "no GL context";
310 return {};
311 }
312
313 if (!gst_video_frame_map(&frame, &videoInfo, buffer, GstMapFlags(GST_MAP_READ))) {
314 qDebug() << "Couldn't map DMA video frame";
315 return {};
316 }
317
318 GlTextures textures = {};
319 textures.owned = true;
320 textures.count = GST_VIDEO_FRAME_N_PLANES(&frame);
321 // int width = GST_VIDEO_FRAME_WIDTH(&frame);
322 // int height = GST_VIDEO_FRAME_HEIGHT(&frame);
323 Q_ASSERT(GST_VIDEO_FRAME_N_PLANES(&frame) == gst_buffer_n_memory(buffer));
324
325 QOpenGLFunctions functions(glContext);
326 functions.glGenTextures(int(textures.count), textures.names.data());
327
328 // qDebug() << Qt::hex << "glGenTextures: glerror" << glGetError() << "egl error" << eglGetError();
329 // qDebug() << "converting DMA buffer nPlanes=" << nPlanes << m_textures[0] << m_textures[1] << m_textures[2];
330
331 for (int i = 0; i < int(textures.count); ++i) {
332 auto offset = GST_VIDEO_FRAME_PLANE_OFFSET(&frame, i);
333 auto stride = GST_VIDEO_FRAME_PLANE_STRIDE(&frame, i);
334 int planeWidth = GST_VIDEO_FRAME_COMP_WIDTH(&frame, i);
335 int planeHeight = GST_VIDEO_FRAME_COMP_HEIGHT(&frame, i);
336 auto mem = gst_buffer_peek_memory(buffer, i);
337 int fd = gst_dmabuf_memory_get_fd(mem);
338
339 // qDebug() << " plane" << i << "size" << width << height << "stride" << stride << "offset" << offset << "fd=" << fd;
340 // ### do we need to open/close the fd?
341 // ### can we convert several planes at once?
342 // Get the correct DRM_FORMATs from the texture format in the description
343 EGLAttrib const attribute_list[] = {
344 EGL_WIDTH, planeWidth,
345 EGL_HEIGHT, planeHeight,
346 EGL_LINUX_DRM_FOURCC_EXT, fourccFromVideoInfo(&videoInfo, i),
347 EGL_DMA_BUF_PLANE0_FD_EXT, fd,
348 EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLAttrib)offset,
349 EGL_DMA_BUF_PLANE0_PITCH_EXT, stride,
350 EGL_NONE
351 };
352 EGLImage image = eglCreateImage(eglDisplay,
353 EGL_NO_CONTEXT,
354 EGL_LINUX_DMA_BUF_EXT,
355 nullptr,
356 attribute_list);
357 if (image == EGL_NO_IMAGE_KHR) {
358 qWarning() << "could not create EGL image for plane" << i << Qt::hex << eglGetError();
359 }
360 // qDebug() << Qt::hex << "eglCreateImage: glerror" << glGetError() << "egl error" << eglGetError();
361 functions.glBindTexture(GL_TEXTURE_2D, textures.names[i]);
362 // qDebug() << Qt::hex << "bind texture: glerror" << glGetError() << "egl error" << eglGetError();
363 auto EGLImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglImageTargetTexture2D;
364 EGLImageTargetTexture2D(GL_TEXTURE_2D, image);
365 // qDebug() << Qt::hex << "glerror" << glGetError() << "egl error" << eglGetError();
366 eglDestroyImage(eglDisplay, image);
367 }
368 gst_video_frame_unmap(&frame);
369
370 return textures;
371}
372#endif
373#endif
374
375std::unique_ptr<QVideoFrameTextures> QGstVideoBuffer::mapTextures(QRhi *rhi)
376{
377 if (!rhi)
378 return {};
379
380#if QT_CONFIG(gstreamer_gl)
381 GlTextures textures = {};
382 if (memoryFormat == QGstCaps::GLTexture)
383 textures = mapFromGlTexture(m_buffer, m_frame, m_videoInfo);
384
385# if GST_GL_HAVE_PLATFORM_EGL && QT_CONFIG(linux_dmabuf)
386 else if (memoryFormat == QGstCaps::DMABuf)
387 textures = mapFromDmaBuffer(m_rhi, m_buffer, m_frame, m_videoInfo, eglDisplay,
388 eglImageTargetTexture2D);
389
390# endif
391 if (textures.count > 0)
392 return std::make_unique<QGstQVideoFrameTextures>(rhi, QSize{m_videoInfo.width, m_videoInfo.height},
393 m_frameFormat.pixelFormat(), textures);
394#endif
395 return {};
396}
397
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
QRhi * rhi() const
Returns the QRhi instance.
MemoryFormat
Definition qgst_p.h:353
@ DMABuf
Definition qgst_p.h:353
@ GLTexture
Definition qgst_p.h:353
std::unique_ptr< QVideoFrameTextures > mapTextures(QRhi *) override
QVideoFrame::MapMode mapMode() const override
void unmap() override
Releases the memory mapped by the map() function.
QGstVideoBuffer(QGstBufferHandle buffer, const GstVideoInfo &info, QGstreamerVideoSink *sink, const QVideoFrameFormat &frameFormat, QGstCaps::MemoryFormat format)
MapData map(QVideoFrame::MapMode mode) override
Independently maps the planes of a video buffer to memory.
static QOpenGLContext * currentContext()
Returns the last context which called makeCurrent in the current thread, or \nullptr,...
The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API.
\variable QRhiGles2InitParams::format
\inmodule QtGui
Definition qrhi.h:895
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10562
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10137
\inmodule QtCore
Definition qsize.h:25
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
PixelFormat
Enumerates video data types.
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of frames in a video stream.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Definition qvideoframe.h:37
EGLContext ctx
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.
void * HANDLE
Definition image.cpp:4
intptr_t EGLAttrib
#define DRM_FORMAT_BGR888
#define DRM_FORMAT_R16
#define DRM_FORMAT_RGB565
#define DRM_FORMAT_RGB888
#define DRM_FORMAT_R8
#define DRM_FORMAT_ABGR8888
#define DRM_FORMAT_GR88
#define DRM_FORMAT_RG88
#define DRM_FORMAT_BGRA1010102
#define DRM_FORMAT_GR1616
#define DRM_FORMAT_RGBA8888
#define DRM_FORMAT_RG1616
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint const GLuint GLuint const GLuint * textures
GLenum GLenum GLsizei count
const void GLsizei GLsizei stride
GLenum GLuint buffer
GLbitfield flags
GLenum GLuint texture
GLenum GLuint GLintptr offset
GLuint64 GLenum GLint fd
GLint GLsizei GLsizei GLenum format
GLuint GLuint * names
GLsizei GLenum GLboolean sink
static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame, unsigned char *baseAddress)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
unsigned int uint
Definition qtypes.h:34
QObject::connect nullptr
QFrame frame
[0]
QHostInfo info
[0]
Type get() const noexcept