Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qgstreamervideosink.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
7#include <qgstutils_p.h>
8#include <rhi/qrhi.h>
9
10#if QT_CONFIG(gstreamer_gl)
11#include <QGuiApplication>
12#include <QtGui/qopenglcontext.h>
13#include <QWindow>
14#include <qpa/qplatformnativeinterface.h>
15#include <gst/gl/gstglconfig.h>
16
17#if GST_GL_HAVE_WINDOW_X11 && __has_include("X11/Xlib-xcb.h")
18# include <gst/gl/x11/gstgldisplay_x11.h>
19#endif
20#if GST_GL_HAVE_PLATFORM_EGL
21# include <gst/gl/egl/gstgldisplay_egl.h>
22# include <EGL/egl.h>
23# include <EGL/eglext.h>
24#endif
25#if GST_GL_HAVE_WINDOW_WAYLAND && __has_include("wayland-client.h")
26# include <gst/gl/wayland/gstgldisplay_wayland.h>
27#endif
28#endif // #if QT_CONFIG(gstreamer_gl)
29
30#include <QtCore/qdebug.h>
31
32#include <QtCore/qloggingcategory.h>
33
35
36static Q_LOGGING_CATEGORY(qLcMediaVideoSink, "qt.multimedia.videosink")
37
40{
41 sinkBin = QGstBin("videoSinkBin");
42 // This is a hack for some iMX and NVidia platforms. These require the use of a special video
43 // conversion element in the pipeline before the video sink, as they unfortunately
44 // output some proprietary format from the decoder even though it's sometimes marked as
45 // a regular supported video/x-raw format.
46 //
47 // To fix this, simply insert the element into the pipeline if it's available. Otherwise
48 // we simply use an identity element.
49 gstQueue = QGstElement("queue");
50 auto imxVideoConvert = QGstElement("imxvideoconvert_g2d");
51 auto nvidiaVideoConvert = QGstElement("nvvidconv");
52 if (!imxVideoConvert.isNull())
53 gstPreprocess = imxVideoConvert;
54 else if (!nvidiaVideoConvert.isNull())
55 gstPreprocess = nvidiaVideoConvert;
56 else
57 gstPreprocess = QGstElement("identity");
58 sinkBin.add(gstQueue, gstPreprocess);
59 gstQueue.link(gstPreprocess);
60 sinkBin.addGhostPad(gstQueue, "sink");
61
62 gstSubtitleSink = GST_ELEMENT(QGstSubtitleSink::createSink(this));
63}
64
66{
67 unrefGstContexts();
68
70}
71
73{
74 updateSinkElement();
75 return sinkBin;
76}
77
79{
80 gstPipeline = pipeline;
81}
82
84{
85 if (gstPipeline.isNull())
86 return true;
87 return gstPipeline.inStoppedState();
88}
89
91{
92 if (rhi && rhi->backend() != QRhi::OpenGLES2)
93 rhi = nullptr;
94 if (m_rhi == rhi)
95 return;
96
97 m_rhi = rhi;
98 updateGstContexts();
99 if (!gstQtSink.isNull()) {
100 // force creation of a new sink with proper caps
101 createQtSink();
102 updateSinkElement();
103 }
104}
105
106void QGstreamerVideoSink::createQtSink()
107{
108 gstQtSink = QGstElement(reinterpret_cast<GstElement *>(QGstVideoRendererSink::createSink(this)));
109}
110
111void QGstreamerVideoSink::updateSinkElement()
112{
113 QGstElement newSink;
114 if (gstQtSink.isNull())
115 createQtSink();
116 newSink = gstQtSink;
117
118 if (newSink == gstVideoSink)
119 return;
120
121 gstPipeline.beginConfig();
122
123 if (!gstVideoSink.isNull()) {
124 gstVideoSink.setStateSync(GST_STATE_NULL);
125 sinkBin.remove(gstVideoSink);
126 }
127
128 gstVideoSink = newSink;
129 sinkBin.add(gstVideoSink);
130 if (!gstPreprocess.link(gstVideoSink))
131 qCDebug(qLcMediaVideoSink) << "couldn't link preprocess and sink";
132 gstVideoSink.setState(GST_STATE_PAUSED);
133
134 gstPipeline.endConfig();
135 gstPipeline.dumpGraph("updateVideoSink");
136}
137
138void QGstreamerVideoSink::unrefGstContexts()
139{
140 if (m_gstGlDisplayContext)
141 gst_context_unref(m_gstGlDisplayContext);
142 m_gstGlDisplayContext = nullptr;
143 if (m_gstGlLocalContext)
144 gst_context_unref(m_gstGlLocalContext);
145 m_gstGlLocalContext = nullptr;
146 m_eglDisplay = nullptr;
147 m_eglImageTargetTexture2D = nullptr;
148}
149
150void QGstreamerVideoSink::updateGstContexts()
151{
152 unrefGstContexts();
153
154#if QT_CONFIG(gstreamer_gl)
155 if (!m_rhi || m_rhi->backend() != QRhi::OpenGLES2)
156 return;
157
158 auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(m_rhi->nativeHandles());
159 auto glContext = nativeHandles->context;
160 Q_ASSERT(glContext);
161
164 m_eglDisplay = pni->nativeResourceForIntegration("egldisplay");
165// qDebug() << "platform is" << platform << m_eglDisplay;
166
167 GstGLDisplay *gstGlDisplay = nullptr;
168 const char *contextName = "eglcontext";
169 GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL;
170 // use the egl display if we have one
171 if (m_eglDisplay) {
172#if GST_GL_HAVE_PLATFORM_EGL
173 gstGlDisplay = (GstGLDisplay *)gst_gl_display_egl_new_with_egl_display(m_eglDisplay);
174 m_eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
175#endif
176 } else {
177 auto display = pni->nativeResourceForIntegration("display");
178
179 if (display) {
180#if GST_GL_HAVE_WINDOW_X11 && __has_include("X11/Xlib-xcb.h")
181 if (platform == QLatin1String("xcb")) {
182 contextName = "glxcontext";
183 glPlatform = GST_GL_PLATFORM_GLX;
184
185 gstGlDisplay = (GstGLDisplay *)gst_gl_display_x11_new_with_display((Display *)display);
186 }
187#endif
188#if GST_GL_HAVE_WINDOW_WAYLAND && __has_include("wayland-client.h")
189 if (platform.startsWith(QLatin1String("wayland"))) {
190 Q_ASSERT(!gstGlDisplay);
191 gstGlDisplay = (GstGLDisplay *)gst_gl_display_wayland_new_with_display((struct wl_display *)display);
192 }
193#endif
194 }
195 }
196
197 if (!gstGlDisplay) {
198 qWarning() << "Could not create GstGLDisplay";
199 return;
200 }
201
202 void *nativeContext = pni->nativeResourceForContext(contextName, glContext);
203 if (!nativeContext)
204 qWarning() << "Could not find resource for" << contextName;
205
206 GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2;
207 GstGLContext *appContext = gst_gl_context_new_wrapped(gstGlDisplay, (guintptr)nativeContext, glPlatform, glApi);
208 if (!appContext)
209 qWarning() << "Could not create wrappped context for platform:" << glPlatform;
210
211 GstGLContext *displayContext = nullptr;
212 GError *error = nullptr;
213 gst_gl_display_create_context(gstGlDisplay, appContext, &displayContext, &error);
214 if (error) {
215 qWarning() << "Could not create display context:" << error->message;
216 g_clear_error(&error);
217 }
218
219 if (appContext)
220 gst_object_unref(appContext);
221
222 m_gstGlDisplayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, false);
223 gst_context_set_gl_display(m_gstGlDisplayContext, gstGlDisplay);
224 gst_object_unref(gstGlDisplay);
225
226 m_gstGlLocalContext = gst_context_new("gst.gl.local_context", false);
227 GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext);
228 gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, displayContext, nullptr);
229 gst_object_unref(displayContext);
230
231 if (!gstPipeline.isNull())
232 gst_element_set_context(gstPipeline.element(), m_gstGlLocalContext);
233#endif // #if QT_CONFIG(gstreamer_gl)
234}
235
237
238#include "moc_qgstreamervideosink_p.cpp"
void remove(const QGstElement &element)
Definition qgst_p.h:561
void add(const QGstElement &element)
Definition qgst_p.h:549
GstStateChangeReturn setState(GstState state)
Definition qgst_p.h:462
GstElement * element() const
Definition qgst_p.h:526
bool setStateSync(GstState state)
Definition qgst_p.h:463
bool link(const QGstElement &next)
Definition qgst_p.h:429
bool isNull() const
Definition qgst_p.h:288
bool inStoppedState() const
void dumpGraph(const char *fileName)
static QGstSubtitleSink * createSink(QGstreamerVideoSink *sink)
static QGstVideoRendererSink * createSink(QGstreamerVideoSink *surface)
void setPipeline(QGstPipeline pipeline)
void setRhi(QRhi *rhi) override
static QPlatformNativeInterface * platformNativeInterface()
QString platformName
The name of the underlying platform plugin.
static OpenGLModuleType openGLModuleType()
Returns the underlying OpenGL implementation type.
The QPlatformNativeInterface class provides an abstraction for retrieving native resource handles.
virtual void * nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context)
virtual void * nativeResourceForIntegration(const QByteArray &resource)
\variable QRhiGles2InitParams::format
\inmodule QtGui
Definition qrhi.h:1767
Implementation backend() const
Definition qrhi.cpp:8289
@ OpenGLES2
Definition qrhi.h:1772
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:9708
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
The QVideoSink class represents a generic sink for video data.
Definition qvideosink.h:22
struct wl_display * display
Definition linuxdmabuf.h:41
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
struct _XDisplay Display
QT_BEGIN_NAMESPACE Platform platform()
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent