4#include <common/qgstreamervideosink_p.h>
5#include <common/qgstvideorenderersink_p.h>
6#include <common/qgst_debug_p.h>
7#include <common/qgstutils_p.h>
10#include <QtCore/qdebug.h>
11#include <QtCore/qloggingcategory.h>
13#if QT_CONFIG(gstreamer_gl)
14# include <QtGui/qguiapplication.h>
15# include <QtGui/qopenglcontext.h>
16# include <QtGui/qwindow.h>
17# include <QtGui/qpa/qplatformnativeinterface.h>
18# include <gst/gl/gstglconfig.h>
19# include <gst/gl/gstgldisplay.h>
21# if QT_CONFIG(gstreamer_gl_x11)
22# include <gst/gl/x11/gstgldisplay_x11.h>
24# if QT_CONFIG(gstreamer_gl_egl)
25# include <gst/gl/egl/gstgldisplay_egl.h>
27# include <EGL/eglext.h>
29# if QT_CONFIG(gstreamer_gl_wayland)
30# include <gst/gl/wayland/gstgldisplay_wayland.h>
36Q_STATIC_LOGGING_CATEGORY(qLcGstVideoSink,
"qt.multimedia.gstvideosink");
43 QGstBin::create(
"videoSinkBin"),
53 QGstElementFactoryHandle factory;
57 QByteArray preprocessOverride = qgetenv(
"QT_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT");
58 if (!preprocessOverride.isEmpty()) {
59 qCDebug(qLcGstVideoSink) <<
"requesting conversion element from environment:"
60 << preprocessOverride;
62 m_gstPreprocess = QGstBin::createFromPipelineDescription(preprocessOverride,
nullptr,
65 qCWarning(qLcGstVideoSink) <<
"Cannot create conversion element:" << preprocessOverride;
68 if (!m_gstPreprocess) {
73 static constexpr auto decodersToTest = {
74 "imxvideoconvert_g2d",
78 for (
const char *decoder : decodersToTest) {
79 factory = QGstElement::findFactory(decoder);
85 qCDebug(qLcGstVideoSink)
86 <<
"instantiating conversion element:"
87 << g_type_name(gst_element_factory_get_element_type(factory.get()));
89 m_gstPreprocess = QGstElement::createFromFactory(factory,
"preprocess");
93 bool disablePixelAspectRatio =
94 qEnvironmentVariableIsSet(
"QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO");
95 if (disablePixelAspectRatio) {
103 QGstElement::createFromFactory(
"identity",
"nullPixelAspectRatioCapsFilter");
106 QGstElement::createFromFactory(
"capsfilter",
"pixelAspectRatioCapsFilter");
108 gst_caps_new_simple(
"video/x-raw",
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL),
111 g_object_set(m_gstCapsFilter.element(),
"caps", capsFilterCaps.caps(), NULL);
114 if (m_gstPreprocess) {
115 m_sinkBin.add(m_gstPreprocess, m_gstCapsFilter);
116 qLinkGstElements(m_gstPreprocess, m_gstCapsFilter);
117 m_sinkBin.addGhostPad(m_gstPreprocess,
"sink");
119 m_sinkBin.add(m_gstCapsFilter);
120 m_sinkBin.addGhostPad(m_gstCapsFilter,
"sink");
126 emit aboutToBeDestroyed();
133 if (!m_gstVideoSink) {
137 updateSinkElement(m_gstQtSink);
145 if (m_isActive == isActive)
147 m_isActive = isActive;
150 m_gstQtSink.setActive(isActive);
155 m_sinkIsAsync = isAsync;
160 if (rhi && rhi->backend() != QRhi::OpenGLES2)
172 updateSinkElement(m_gstQtSink);
178 Q_ASSERT(!m_gstQtSink);
180 m_gstQtSink = QGstVideoRendererSink::createSink(
this);
182 m_gstQtSink.set(
"async",
false);
183 m_gstQtSink.setActive(m_isActive);
188 if (newSink == m_gstVideoSink)
191 m_gstCapsFilter.src().modifyPipelineInIdleProbe([&] {
193 m_sinkBin.stopAndRemoveElements(m_gstVideoSink);
195 m_gstVideoSink = std::move(newSink);
196 m_sinkBin.add(m_gstVideoSink);
197 qLinkGstElements(m_gstCapsFilter, m_gstVideoSink);
198 GstEvent *event = gst_event_new_reconfigure();
199 gst_element_send_event(m_gstVideoSink.element(), event);
200 m_gstVideoSink.syncStateWithParent();
203 m_sinkBin.dumpPipelineGraph(
"updateSinkElement");
208 m_gstGlDisplayContext.reset();
209 m_gstGlLocalContext.reset();
210 m_eglDisplay =
nullptr;
211 m_eglImageTargetTexture2D =
nullptr;
216 using namespace Qt::Literals;
220#if QT_CONFIG(gstreamer_gl)
221 if (!m_rhi || m_rhi->backend() != QRhi::OpenGLES2)
224 auto *nativeHandles =
static_cast<
const QRhiGles2NativeHandles *>(m_rhi->nativeHandles());
225 auto glContext = nativeHandles->context;
228 const QString platform = QGuiApplication::platformName();
229 QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface();
230 m_eglDisplay = pni->nativeResourceForIntegration(
"egldisplay"_ba);
233 QGstGLDisplayHandle gstGlDisplay;
235 QByteArray contextName =
"eglcontext"_ba;
236 GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL;
239# if QT_CONFIG(gstreamer_gl_egl)
241 GST_GL_DISPLAY_CAST(gst_gl_display_egl_new_with_egl_display(m_eglDisplay)),
242 QGstGLDisplayHandle::HasRef);
243 m_eglImageTargetTexture2D = eglGetProcAddress(
"glEGLImageTargetTexture2DOES");
246 auto display = pni->nativeResourceForIntegration(
"display"_ba);
249# if QT_CONFIG(gstreamer_gl_x11)
250 if (platform == QLatin1String(
"xcb")) {
251 contextName =
"glxcontext"_ba;
252 glPlatform = GST_GL_PLATFORM_GLX;
254 gstGlDisplay.reset(GST_GL_DISPLAY_CAST(gst_gl_display_x11_new_with_display(
255 reinterpret_cast<Display *>(display))),
256 QGstGLDisplayHandle::HasRef);
259# if QT_CONFIG(gstreamer_gl_wayland)
260 if (platform.startsWith(QLatin1String(
"wayland"))) {
261 Q_ASSERT(!gstGlDisplay);
262 gstGlDisplay.reset(GST_GL_DISPLAY_CAST(gst_gl_display_wayland_new_with_display(
263 reinterpret_cast<
struct wl_display *>(display))),
264 QGstGLDisplayHandle::HasRef);
271 qWarning() <<
"Could not create GstGLDisplay";
275 void *nativeContext = pni->nativeResourceForContext(contextName, glContext);
277 qWarning() <<
"Could not find resource for" << contextName;
279 GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2;
280 QGstGLContextHandle appContext{
281 gst_gl_context_new_wrapped(gstGlDisplay.get(), guintptr(nativeContext), glPlatform, glApi),
282 QGstGLContextHandle::HasRef,
285 qWarning() <<
"Could not create wrappped context for platform:" << glPlatform;
287 gst_gl_context_activate(appContext.get(),
true);
289 QUniqueGErrorHandle error;
290 gst_gl_context_fill_info(appContext.get(), &error);
292 qWarning() <<
"Could not fill context info:" << error;
296 QGstGLContextHandle displayContext;
297 gst_gl_display_create_context(gstGlDisplay.get(), appContext.get(), &displayContext, &error);
299 qWarning() <<
"Could not create display context:" << error;
303 m_gstGlDisplayContext.reset(gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE,
false),
304 QGstContextHandle::HasRef);
305 gst_context_set_gl_display(m_gstGlDisplayContext.get(), gstGlDisplay.get());
307 m_gstGlLocalContext.reset(gst_context_new(
"gst.gl.local_context",
false),
308 QGstContextHandle::HasRef);
309 GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext.get());
310 gst_structure_set(structure,
"context", GST_TYPE_GL_CONTEXT, displayContext.get(),
nullptr);
311 displayContext.reset();
320#include "moc_qgstreamervideosink_p.cpp"
~QGstreamerVideoSink() override