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>
16#if QT_CONFIG(gstreamer_gl)
17# include <QtGui/qguiapplication.h>
18# include <QtGui/qguiapplication_platform.h>
19# include <QtGui/qopenglcontext.h>
20# include <QtGui/qwindow.h>
21# include <gst/gl/gstglconfig.h>
22# include <gst/gl/gstgldisplay.h>
24# if QT_CONFIG(gstreamer_gl_x11)
25# include <gst/gl/x11/gstgldisplay_x11.h>
27# if QT_CONFIG(gstreamer_gl_egl)
28# include <common/qgstreameregldisplay_p.h>
29# include <QtMultimedia/private/qeglimagefunctions_p.h>
30# include <QtMultimedia/private/qmultimedia_gl_support_p.h>
31# include <gst/gl/egl/gstgldisplay_egl.h>
33# include <EGL/eglext.h>
35# if QT_CONFIG(gstreamer_gl_wayland)
36# include <gst/gl/wayland/gstgldisplay_wayland.h>
42Q_STATIC_LOGGING_CATEGORY(qLcGstVideoSink,
"qt.multimedia.gstvideosink");
59 QGstElementFactoryHandle factory;
63 QByteArray preprocessOverride = qgetenv(
"QT_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT");
64 if (!preprocessOverride.isEmpty()) {
65 qCDebug(qLcGstVideoSink) <<
"requesting conversion element from environment:"
66 << preprocessOverride;
68 m_gstPreprocess = QGstBin::createFromPipelineDescription(preprocessOverride,
nullptr,
71 qCWarning(qLcGstVideoSink) <<
"Cannot create conversion element:" << preprocessOverride;
74 if (!m_gstPreprocess) {
79 static constexpr auto decodersToTest = {
80 "imxvideoconvert_g2d",
84 for (
const char *decoder : decodersToTest) {
85 factory = QGstElement::findFactory(decoder);
91 qCDebug(qLcGstVideoSink)
92 <<
"instantiating conversion element:"
93 << g_type_name(gst_element_factory_get_element_type(factory.get()));
95 m_gstPreprocess = QGstElement::createFromFactory(factory,
"preprocess");
99 bool disablePixelAspectRatio =
100 qEnvironmentVariableIntValue(
"QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO") != 0;
101 if (disablePixelAspectRatio) {
109 QGstElement::createFromFactory(
"identity",
"nullPixelAspectRatioCapsFilter");
112 QGstElement::createFromFactory(
"capsfilter",
"pixelAspectRatioCapsFilter");
114 gst_caps_new_simple(
"video/x-raw",
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL),
117 g_object_set(m_gstCapsFilter.element(),
"caps", capsFilterCaps.caps(), NULL);
120 if (m_gstPreprocess) {
121 m_sinkBin.add(m_gstPreprocess, m_gstCapsFilter);
122 qLinkGstElements(m_gstPreprocess, m_gstCapsFilter);
123 m_sinkBin.addGhostPad(m_gstPreprocess,
"sink");
125 m_sinkBin.add(m_gstCapsFilter);
126 m_sinkBin.addGhostPad(m_gstCapsFilter,
"sink");
134 Q_EMIT aboutToBeDestroyed();
141 if (!m_gstVideoSink) {
145 updateSinkElement(m_gstQtSink);
153 if (m_isActive == isActive)
155 m_isActive = isActive;
158 m_gstQtSink.setActive(isActive);
163 m_sinkIsAsync = isAsync;
168 Q_ASSERT(pluggableSink);
169 m_pluggableVideoSink = pluggableSink;
170 m_pluggableVideoSink->setVideoFrame(m_currentVideoFrame);
171 m_pluggableVideoSink->setSubtitleText(m_currentSubtitleText);
172 m_pluggableVideoSink->setNativeSize(m_currentNativeSize);
173 connect(
this, &QGstreamerRelayVideoSink::videoFrameChanged,
174 m_pluggableVideoSink, &QPlatformVideoSink::setVideoFrame);
175 connect(
this, &QGstreamerRelayVideoSink::subtitleTextChanged,
176 m_pluggableVideoSink, &QPlatformVideoSink::setSubtitleText);
178 m_pluggableVideoSink, &QPlatformVideoSink::setNativeSize);
182 m_pluggableVideoSink->withRhi([&](QRhi *rhi) {
183 renderingRhiChanged(rhi);
187 connect(m_pluggableVideoSink, &QRhiVideoSink::rhiChanged,
this, [
this](QRhi *rhi) {
188 renderingRhiChanged(rhi);
194 if (m_pluggableVideoSink) {
195 disconnect(
this, &QGstreamerRelayVideoSink::videoFrameChanged,
196 m_pluggableVideoSink, &QPlatformVideoSink::setVideoFrame);
197 disconnect(
this, &QGstreamerRelayVideoSink::subtitleTextChanged,
198 m_pluggableVideoSink, &QPlatformVideoSink::setSubtitleText);
200 m_pluggableVideoSink, &QPlatformVideoSink::setNativeSize);
201 disconnect(m_rhiConnection);
202 m_pluggableVideoSink =
nullptr;
208 Q_EMIT videoFrameChanged(frame);
209 m_currentVideoFrame = frame;
214 Q_EMIT subtitleTextChanged(subtitleText);
215 m_currentSubtitleText = subtitleText;
220 Q_EMIT nativeSizeChanged(size);
221 m_currentNativeSize = size;
226 Q_ASSERT(!m_gstQtSink);
228 m_gstQtSink = QGstVideoRendererSink::createSink(
this);
230 m_gstQtSink.set(
"async",
false);
231 m_gstQtSink.setActive(m_isActive);
236 if (newSink == m_gstVideoSink)
239 m_gstCapsFilter.src().modifyPipelineInIdleProbe([&] {
241 m_sinkBin.stopAndRemoveElements(m_gstVideoSink);
243 m_gstVideoSink = std::move(newSink);
244 m_sinkBin.add(m_gstVideoSink);
245 qLinkGstElements(m_gstCapsFilter, m_gstVideoSink);
246 GstEvent *event = gst_event_new_reconfigure();
247 gst_element_send_event(m_gstVideoSink.element(), event);
248 m_gstVideoSink.syncStateWithParent();
251 m_sinkBin.dumpPipelineGraph(
"updateSinkElement");
256 m_gstGlDisplayContext.reset();
257 m_gstGlLocalContext.reset();
260#if QT_CONFIG(gstreamer_gl)
264using AnyNativeGLContextType = std::variant<
265# if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
266 EGLContext, GLXContext
267# elif QT_CONFIG(gstreamer_gl_egl)
269# elif QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
276std::optional<AnyNativeGLContextType>
277resolveNativeGlContext([[maybe_unused]] QOpenGLContext *glContext, GstGLPlatform glPlatform)
279 switch (glPlatform) {
280# if QT_CONFIG(gstreamer_gl_egl)
281 case GST_GL_PLATFORM_EGL:
282 if (
auto *eglContext = glContext->nativeInterface<QNativeInterface::QEGLContext>())
283 return eglContext->nativeContext();
286# if QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
287 case GST_GL_PLATFORM_GLX:
288 if (
auto *glxContext = glContext->nativeInterface<QNativeInterface::QGLXContext>())
289 return glxContext->nativeContext();
297struct ResolveGstGlDisplayResult
299 QGstGLDisplayHandle display;
300 GstGLPlatform platform;
303std::optional<ResolveGstGlDisplayResult> resolveGstGlDisplay([[maybe_unused]] QRhi &rhi)
306# if QT_CONFIG(gstreamer_gl_egl)
307 if (EGLDisplay eglDisplay = QtMultimediaPrivate::resolveEglDisplay(rhi)) {
308 QGstGLDisplayHandle gstGlDisplay{
309 GST_GL_DISPLAY_CAST(gst_gl_display_egl_new_with_egl_display(eglDisplay)),
310 QGstGLDisplayHandle::HasRef,
313 return ResolveGstGlDisplayResult{
314 std::move(gstGlDisplay),
320# if QT_CONFIG(gstreamer_gl_wayland)
321 if (
auto *waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>()) {
322 QGstGLDisplayHandle gstGlDisplay{
323 GST_GL_DISPLAY_CAST(gst_gl_display_wayland_new_with_display(waylandApp->display())),
324 QGstGLDisplayHandle::HasRef,
327 return ResolveGstGlDisplayResult{
328 std::move(gstGlDisplay),
334# if QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
335 if (
auto *x11App = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()) {
336 QGstGLDisplayHandle gstGlDisplay{
337 GST_GL_DISPLAY_CAST(gst_gl_display_x11_new_with_display(x11App->display())),
338 QGstGLDisplayHandle::HasRef,
341 return ResolveGstGlDisplayResult{
342 std::move(gstGlDisplay),
351QGstGLContextHandle resolveGstGLContextHandle(
const QGstGLDisplayHandle &gstGlDisplay,
352 AnyNativeGLContextType nativeContext,
353 GstGLPlatform glPlatform)
355 GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL
358 guintptr nativeContextArgument = std::visit([](
auto nativeContext) {
359 return guintptr(nativeContext);
362 QGstGLContextHandle appContext{
363 gst_gl_context_new_wrapped(gstGlDisplay.get(), nativeContextArgument, glPlatform, glApi),
364 QGstGLContextHandle::HasRef,
376 QRhi *currentRhi = rhi();
380#if QT_CONFIG(gstreamer_gl)
381 if (!currentRhi || currentRhi->backend() != QRhi::OpenGLES2)
383 QOpenGLContext *glContext =
384 static_cast<
const QRhiGles2NativeHandles *>(currentRhi->nativeHandles())->context;
387 std::optional resolvedDisplay = resolveGstGlDisplay(*currentRhi);
388 if (!resolvedDisplay) {
389 qWarning() <<
"Could not resolve GstGLDisplay";
393 std::optional nativeContext = resolveNativeGlContext(glContext, resolvedDisplay->platform);
394 if (!nativeContext) {
395 qWarning() <<
"Could not find native gl context";
399 QGstGLContextHandle appContext = resolveGstGLContextHandle(
400 resolvedDisplay->display, *nativeContext, resolvedDisplay->platform);
402 qWarning() <<
"Could not create wrappped context for platform:"
403 << resolvedDisplay->platform;
407 gst_gl_context_activate(appContext.get(),
true);
409 QUniqueGErrorHandle error;
410 gst_gl_context_fill_info(appContext.get(), &error);
412 qWarning() <<
"Could not fill context info:" << error;
416 QGstGLContextHandle displayContext;
417 gst_gl_display_create_context(resolvedDisplay->display.get(), appContext.get(), &displayContext,
420 qWarning() <<
"Could not create display context:" << error;
424 m_gstGlDisplayContext = QGstContextHandle{
425 gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE,
false),
426 QGstContextHandle::HasRef,
428 gst_context_set_gl_display(m_gstGlDisplayContext.get(), resolvedDisplay->display.get());
430 m_gstGlLocalContext = QGstContextHandle{
431 gst_context_new(
"gst.gl.local_context",
false),
432 QGstContextHandle::HasRef,
435 GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext.get());
436 gst_structure_set(structure,
"context", GST_TYPE_GL_CONTEXT, displayContext.get(),
nullptr);
437 displayContext.reset();
452#include "moc_qgstreamervideosink_p.cpp"
void setNativeSize(QSize size)
QGstreamerRelayVideoSink(QObject *parent=nullptr)
~QGstreamerRelayVideoSink()
void setSubtitleText(const QString &subtitleText)
void connectPluggableVideoSink(QGstreamerPluggableVideoSink *pluggableSink)
void nativeSizeChanged(QSize size)
void disconnectPluggableVideoSink()
Combined button and popup list for selecting options.