4#if defined(QGC_HAS_GST_GLMEMORY_GPU_PATH)
8#include <QtCore/QLoggingCategory>
9#include <QtCore/QMutex>
10#include <QtCore/QMutexLocker>
11#include <QtGui/QGuiApplication>
12#include <QtGui/QOpenGLContext>
13#include <qpa/qplatformnativeinterface.h>
17# include <gst/gl/egl/gstgldisplay_egl.h>
20# if __has_include(<gst/gl/x11/gstgldisplay_x11.h>) && __has_include(<QtGui/qopenglcontext_platform.h>)
21# include <gst/gl/x11/gstgldisplay_x11.h>
22# include <QtGui/qopenglcontext_platform.h>
24# define QGC_GST_BRIDGE_HAS_GLX 1
27# if __has_include(<gst/gl/wayland/gstgldisplay_wayland.h>)
28# include <gst/gl/wayland/gstgldisplay_wayland.h>
29# define QGC_GST_BRIDGE_HAS_WAYLAND 1
35namespace GstGlContextBridge {
39GstGLDisplay *s_display =
nullptr;
40GstGLContext *s_context =
nullptr;
42bool s_primeAttempted =
false;
44int s_primeNullShareCount = 0;
45constexpr int kMaxPrimeNullShareRetries = 16;
48EGLDisplay qtEglDisplay()
50 if (
auto *ni = QGuiApplication::platformNativeInterface()) {
51 if (
auto *d = ni->nativeResourceForIntegration(
"egldisplay")) {
52 return static_cast<EGLDisplay
>(d);
55 return eglGetDisplay(EGL_DEFAULT_DISPLAY);
58EGLContext qtEglContext(QOpenGLContext *qtCtx)
60 if (!qtCtx)
return EGL_NO_CONTEXT;
61 if (
auto *egl = qtCtx->nativeInterface<QNativeInterface::QEGLContext>()) {
62 return egl->nativeContext();
64 return EGL_NO_CONTEXT;
70 if (s_primed)
return true;
71 if (s_primeAttempted)
return false;
72 s_primeAttempted =
true;
74 QOpenGLContext *qtCtx = QOpenGLContext::globalShareContext();
76 ++s_primeNullShareCount;
77 if (s_primeNullShareCount <= kMaxPrimeNullShareRetries) {
78 qCInfo(GstGlBridgeLog) <<
"globalShareContext() is null — Qt GL not initialized yet"
79 <<
"(attempt" << s_primeNullShareCount <<
"/" << kMaxPrimeNullShareRetries <<
")";
80 s_primeAttempted =
false;
82 if (s_primeNullShareCount == kMaxPrimeNullShareRetries + 1) {
83 qCWarning(GstGlBridgeLog) <<
"globalShareContext() still null after"
84 << kMaxPrimeNullShareRetries
85 <<
"retries; GL bridge giving up";
94 EGLContext eglCtx = qtEglContext(qtCtx);
95 EGLDisplay eglDisp = (eglCtx != EGL_NO_CONTEXT) ? qtEglDisplay() : EGL_NO_DISPLAY;
100 auto bail = [](
const char *) ->
bool { s_primeAttempted =
false;
return false; };
101 if (eglCtx != EGL_NO_CONTEXT && eglDisp != EGL_NO_DISPLAY) {
102# if defined(QGC_GST_BRIDGE_HAS_WAYLAND)
105 const QString platformName = QGuiApplication::platformName();
106 if (platformName == QLatin1String(
"wayland") || platformName == QLatin1String(
"wayland-egl")) {
107 struct wl_display *wlDisp =
nullptr;
108 if (
auto *ni = QGuiApplication::platformNativeInterface()) {
109 wlDisp =
static_cast<struct wl_display *
>(ni->nativeResourceForIntegration(
"wl_display"));
112 GstGLDisplayWayland *displayWl = gst_gl_display_wayland_new_with_display(wlDisp);
114 s_display = GST_GL_DISPLAY(displayWl);
115# if GST_CHECK_VERSION(1, 26, 0)
119 if (GstGLDisplayEGL *derived = gst_gl_display_egl_from_gl_display(s_display)) {
120 gst_gl_display_egl_set_foreign(derived, TRUE);
121 gst_object_unref(derived);
129 GstGLDisplayEGL *displayEgl = gst_gl_display_egl_new_with_egl_display(eglDisp);
131 qCWarning(GstGlBridgeLog) <<
"gst_gl_display_egl_new_with_egl_display failed";
132 return bail(
"displayEgl");
134 s_display = GST_GL_DISPLAY(displayEgl);
137 s_context = gst_gl_context_new_wrapped(s_display,
138 reinterpret_cast<guintptr
>(eglCtx),
140 static_cast<GstGLAPI
>(GST_GL_API_GLES2 | GST_GL_API_OPENGL));
142 qCWarning(GstGlBridgeLog) <<
"gst_gl_context_new_wrapped (EGL) failed";
143 gst_clear_object(&s_display);
144 return bail(
"ctxEgl");
146# if defined(QGC_GST_BRIDGE_HAS_WAYLAND)
147 const bool isWayland = GST_IS_GL_DISPLAY_WAYLAND(s_display);
148 qCInfo(GstGlBridgeLog) << (isWayland ?
"GL bridge primed (Wayland+EGL)" :
"GL bridge primed (EGL)");
150 qCInfo(GstGlBridgeLog) <<
"GL bridge primed (EGL)";
153# if defined(QGC_GST_BRIDGE_HAS_GLX)
155 auto *glx = qtCtx->nativeInterface<QNativeInterface::QGLXContext>();
157 qCWarning(GstGlBridgeLog) <<
"Qt GL context exposes neither EGL nor GLX; GL bridge disabled";
158 return bail(
"noGlx");
160 Display *xdisp =
nullptr;
161 if (
auto *ni = QGuiApplication::platformNativeInterface()) {
162 xdisp =
static_cast<Display *
>(ni->nativeResourceForIntegration(
"display"));
165 qCWarning(GstGlBridgeLog) <<
"X11 Display unresolvable; GL bridge disabled";
166 return bail(
"xdisp");
168 GstGLDisplayX11 *displayX11 = gst_gl_display_x11_new_with_display(xdisp);
170 qCWarning(GstGlBridgeLog) <<
"gst_gl_display_x11_new_with_display failed";
171 return bail(
"displayX11");
173 s_display = GST_GL_DISPLAY(displayX11);
174 s_context = gst_gl_context_new_wrapped(s_display,
175 reinterpret_cast<guintptr
>(glx->nativeContext()),
177 static_cast<GstGLAPI
>(GST_GL_API_OPENGL));
179 qCWarning(GstGlBridgeLog) <<
"gst_gl_context_new_wrapped (GLX) failed";
180 gst_clear_object(&s_display);
181 return bail(
"ctxGlx");
183 qCInfo(GstGlBridgeLog) <<
"GL bridge primed (GLX)";
185 qCWarning(GstGlBridgeLog) <<
"Qt EGLContext unresolvable and GLX bridge not built; GL bridge disabled";
186 return bail(
"noEglNoGlx");
192 qCDebug(GstGlBridgeLog) <<
"GL bridge primed: display=" << s_display
193 <<
"context=" << s_context;
198 qCInfo(GstGlBridgeLog) <<
"GL bridge inactive on this platform (non-EGL)";
207 QMutexLocker lock(&s_mutex);
208 return primeLocked();
211GstBusSyncReply handleSyncMessage(GstMessage *message)
213 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_NEED_CONTEXT) {
217 const gchar *contextType =
nullptr;
218 if (!gst_message_parse_context_type(message, &contextType) || !contextType) {
221 const bool isGlDisplay = (g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE) == 0);
222 const bool isGlApp = (g_strcmp0(contextType,
"gst.gl.app_context") == 0);
223 if (!isGlDisplay && !isGlApp) {
227 QMutexLocker lock(&s_mutex);
228 if (!primeLocked()) {
232 GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(message));
237 if (isGlDisplay && s_display) {
238 GstContext *gctx = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
239 gst_context_set_gl_display(gctx, s_display);
240 gst_element_set_context(element, gctx);
241 gst_context_unref(gctx);
242 qCDebug(GstGlBridgeLog) <<
"Provided GL display context to" << GST_ELEMENT_NAME(element);
243 }
else if (isGlApp && s_context) {
244 GstContext *gctx = gst_context_new(
"gst.gl.app_context", TRUE);
245 GstStructure *s = gst_context_writable_structure(gctx);
246 gst_structure_set(s,
"context", GST_TYPE_GL_CONTEXT, s_context, NULL);
247 gst_element_set_context(element, gctx);
248 gst_context_unref(gctx);
249 qCDebug(GstGlBridgeLog) <<
"Provided GL app context to" << GST_ELEMENT_NAME(element);
254 gst_message_unref(message);
258bool answerContextQuery(GstQuery *query)
260 if (!query || GST_QUERY_TYPE(query) != GST_QUERY_CONTEXT) {
263 const gchar *contextType =
nullptr;
264 if (!gst_query_parse_context_type(query, &contextType) || !contextType) {
267 const bool isGlDisplay = (g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE) == 0);
268 const bool isGlApp = (g_strcmp0(contextType,
"gst.gl.app_context") == 0);
269 if (!isGlDisplay && !isGlApp) {
273 QMutexLocker lock(&s_mutex);
274 if (!primeLocked()) {
278 if (isGlDisplay && s_display) {
279 GstContext *gctx = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
280 gst_context_set_gl_display(gctx, s_display);
281 gst_query_set_context(query, gctx);
282 gst_context_unref(gctx);
285 if (isGlApp && s_context) {
286 GstContext *gctx = gst_context_new(
"gst.gl.app_context", TRUE);
287 GstStructure *s = gst_context_writable_structure(gctx);
288 gst_structure_set(s,
"context", GST_TYPE_GL_CONTEXT, s_context, NULL);
289 gst_query_set_context(query, gctx);
290 gst_context_unref(gctx);
298 QMutexLocker lock(&s_mutex);
299 gst_clear_object(&s_context);
300 gst_clear_object(&s_display);
302 s_primeAttempted =
false;
303 s_primeNullShareCount = 0;
304 qCDebug(GstGlBridgeLog) <<
"GL bridge reset";
309 QMutexLocker lock(&s_mutex);
310 if (s_primed)
return;
311 if (s_primeAttempted && s_primeNullShareCount > kMaxPrimeNullShareRetries) {
312 s_primeAttempted =
false;
313 s_primeNullShareCount = 0;
314 qCInfo(GstGlBridgeLog) <<
"GL bridge rearm: clearing exhausted retry latch";
320struct GlBridgeRegistrar {
321 GlBridgeRegistrar() {
322 GstContextBridgeRegistry::registerBridgeHandler(&GstGlContextBridge::handleSyncMessage);
323 GstContextBridgeRegistry::registerResetCallback(&GstGlContextBridge::reset);
326static GlBridgeRegistrar s_glBridgeRegistrar;
struct _GstElement GstElement
#define QGC_LOGGING_CATEGORY(name, categoryStr)