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>
45 : QPlatformVideoSink{ parent }
78 QGstElementFactoryHandle factory;
82 QByteArray preprocessOverride = qgetenv(
"QT_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT");
83 if (!preprocessOverride.isEmpty()) {
84 qCDebug(qLcGstVideoSink) <<
"requesting conversion element from environment:"
85 << preprocessOverride;
87 m_gstPreprocess = QGstBin::createFromPipelineDescription(preprocessOverride,
nullptr,
90 qCWarning(qLcGstVideoSink) <<
"Cannot create conversion element:" << preprocessOverride;
93 if (!m_gstPreprocess) {
98 static constexpr auto decodersToTest = {
99 "imxvideoconvert_g2d",
103 for (
const char *decoder : decodersToTest) {
104 factory = QGstElement::findFactory(decoder);
110 qCDebug(qLcGstVideoSink)
111 <<
"instantiating conversion element:"
112 << g_type_name(gst_element_factory_get_element_type(factory.get()));
114 m_gstPreprocess = QGstElement::createFromFactory(factory,
"preprocess");
118 bool disablePixelAspectRatio =
119 qEnvironmentVariableIntValue(
"QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO") != 0;
120 if (disablePixelAspectRatio) {
128 QGstElement::createFromFactory(
"identity",
"nullPixelAspectRatioCapsFilter");
131 QGstElement::createFromFactory(
"capsfilter",
"pixelAspectRatioCapsFilter");
133 gst_caps_new_simple(
"video/x-raw",
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL),
136 g_object_set(m_gstCapsFilter.element(),
"caps", capsFilterCaps.caps(), NULL);
139 if (m_gstPreprocess) {
140 m_sinkBin.add(m_gstPreprocess, m_gstCapsFilter);
141 qLinkGstElements(m_gstPreprocess, m_gstCapsFilter);
142 m_sinkBin.addGhostPad(m_gstPreprocess,
"sink");
144 m_sinkBin.add(m_gstCapsFilter);
145 m_sinkBin.addGhostPad(m_gstCapsFilter,
"sink");
153 emit aboutToBeDestroyed();
160 if (!m_gstVideoSink) {
164 updateSinkElement(m_gstQtSink);
172 if (m_isActive == isActive)
174 m_isActive = isActive;
177 m_gstQtSink.setActive(isActive);
182 m_sinkIsAsync = isAsync;
187 Q_ASSERT(pluggableSink);
188 m_pluggableVideoSink = pluggableSink;
189 m_pluggableVideoSink->setVideoFrame(m_currentVideoFrame);
190 m_pluggableVideoSink->setSubtitleText(m_currentSubtitleText);
191 m_pluggableVideoSink->setNativeSize(m_currentNativeSize);
192 connect(
this, &QGstreamerRelayVideoSink::videoFrameChanged,
193 m_pluggableVideoSink, &QPlatformVideoSink::setVideoFrame);
194 connect(
this, &QGstreamerRelayVideoSink::subtitleTextChanged,
195 m_pluggableVideoSink, &QPlatformVideoSink::setSubtitleText);
197 m_pluggableVideoSink, &QPlatformVideoSink::setNativeSize);
200 renderingRhiChanged(m_pluggableVideoSink->rhi());
201 m_rhiConnection = connect(m_pluggableVideoSink, &QPlatformVideoSink::rhiChanged,
this, [
this](){
202 renderingRhiChanged(m_pluggableVideoSink->rhi());
208 if (m_pluggableVideoSink) {
209 disconnect(
this, &QGstreamerRelayVideoSink::videoFrameChanged,
210 m_pluggableVideoSink, &QPlatformVideoSink::setVideoFrame);
211 disconnect(
this, &QGstreamerRelayVideoSink::subtitleTextChanged,
212 m_pluggableVideoSink, &QPlatformVideoSink::setSubtitleText);
214 m_pluggableVideoSink, &QPlatformVideoSink::setNativeSize);
215 disconnect(m_rhiConnection);
216 m_pluggableVideoSink =
nullptr;
222 emit videoFrameChanged(frame);
223 m_currentVideoFrame = frame;
228 emit subtitleTextChanged(subtitleText);
229 m_currentSubtitleText = subtitleText;
234 emit nativeSizeChanged(size);
235 m_currentNativeSize = size;
240 Q_ASSERT(!m_gstQtSink);
242 m_gstQtSink = QGstVideoRendererSink::createSink(
this);
244 m_gstQtSink.set(
"async",
false);
245 m_gstQtSink.setActive(m_isActive);
250 if (newSink == m_gstVideoSink)
253 m_gstCapsFilter.src().modifyPipelineInIdleProbe([&] {
255 m_sinkBin.stopAndRemoveElements(m_gstVideoSink);
257 m_gstVideoSink = std::move(newSink);
258 m_sinkBin.add(m_gstVideoSink);
259 qLinkGstElements(m_gstCapsFilter, m_gstVideoSink);
260 GstEvent *event = gst_event_new_reconfigure();
261 gst_element_send_event(m_gstVideoSink.element(), event);
262 m_gstVideoSink.syncStateWithParent();
265 m_sinkBin.dumpPipelineGraph(
"updateSinkElement");
270 m_gstGlDisplayContext.reset();
271 m_gstGlLocalContext.reset();
274#if QT_CONFIG(gstreamer_gl)
278using AnyNativeGLContextType = std::variant<
279# if QT_CONFIG(gstreamer_gl_egl) && QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
280 EGLContext, GLXContext
281# elif QT_CONFIG(gstreamer_gl_egl)
283# elif QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
290std::optional<AnyNativeGLContextType>
291resolveNativeGlContext([[maybe_unused]] QOpenGLContext *glContext, GstGLPlatform glPlatform)
293 switch (glPlatform) {
294# if QT_CONFIG(gstreamer_gl_egl)
295 case GST_GL_PLATFORM_EGL:
296 if (
auto *eglContext = glContext->nativeInterface<QNativeInterface::QEGLContext>())
297 return eglContext->nativeContext();
300# if QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
301 case GST_GL_PLATFORM_GLX:
302 if (
auto *glxContext = glContext->nativeInterface<QNativeInterface::QGLXContext>())
303 return glxContext->nativeContext();
311struct ResolveGstGlDisplayResult
313 QGstGLDisplayHandle display;
314 GstGLPlatform platform;
317std::optional<ResolveGstGlDisplayResult> resolveGstGlDisplay([[maybe_unused]] QRhi &rhi)
320# if QT_CONFIG(gstreamer_gl_egl)
321 if (EGLDisplay eglDisplay = QtMultimediaPrivate::resolveEglDisplay(rhi)) {
322 QGstGLDisplayHandle gstGlDisplay{
323 GST_GL_DISPLAY_CAST(gst_gl_display_egl_new_with_egl_display(eglDisplay)),
324 QGstGLDisplayHandle::HasRef,
327 return ResolveGstGlDisplayResult{
328 std::move(gstGlDisplay),
334# if QT_CONFIG(gstreamer_gl_wayland)
335 if (
auto *waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>()) {
336 QGstGLDisplayHandle gstGlDisplay{
337 GST_GL_DISPLAY_CAST(gst_gl_display_wayland_new_with_display(waylandApp->display())),
338 QGstGLDisplayHandle::HasRef,
341 return ResolveGstGlDisplayResult{
342 std::move(gstGlDisplay),
348# if QT_CONFIG(gstreamer_gl_x11) && QT_CONFIG(xcb_glx_plugin)
349 if (
auto *x11App = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()) {
350 QGstGLDisplayHandle gstGlDisplay{
351 GST_GL_DISPLAY_CAST(gst_gl_display_x11_new_with_display(x11App->display())),
352 QGstGLDisplayHandle::HasRef,
355 return ResolveGstGlDisplayResult{
356 std::move(gstGlDisplay),
365QGstGLContextHandle resolveGstGLContextHandle(
const QGstGLDisplayHandle &gstGlDisplay,
366 AnyNativeGLContextType nativeContext,
367 GstGLPlatform glPlatform)
369 GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL
372 guintptr nativeContextArgument = std::visit([](
auto nativeContext) {
373 return guintptr(nativeContext);
376 QGstGLContextHandle appContext{
377 gst_gl_context_new_wrapped(gstGlDisplay.get(), nativeContextArgument, glPlatform, glApi),
378 QGstGLContextHandle::HasRef,
390 QRhi *currentRhi = rhi();
394#if QT_CONFIG(gstreamer_gl)
395 if (!currentRhi || currentRhi->backend() != QRhi::OpenGLES2)
397 QOpenGLContext *glContext =
398 static_cast<
const QRhiGles2NativeHandles *>(currentRhi->nativeHandles())->context;
401 std::optional resolvedDisplay = resolveGstGlDisplay(*currentRhi);
402 if (!resolvedDisplay) {
403 qWarning() <<
"Could not resolve GstGLDisplay";
407 std::optional nativeContext = resolveNativeGlContext(glContext, resolvedDisplay->platform);
408 if (!nativeContext) {
409 qWarning() <<
"Could not find native gl context";
413 QGstGLContextHandle appContext = resolveGstGLContextHandle(
414 resolvedDisplay->display, *nativeContext, resolvedDisplay->platform);
416 qWarning() <<
"Could not create wrappped context for platform:"
417 << resolvedDisplay->platform;
421 gst_gl_context_activate(appContext.get(),
true);
423 QUniqueGErrorHandle error;
424 gst_gl_context_fill_info(appContext.get(), &error);
426 qWarning() <<
"Could not fill context info:" << error;
430 QGstGLContextHandle displayContext;
431 gst_gl_display_create_context(resolvedDisplay->display.get(), appContext.get(), &displayContext,
434 qWarning() <<
"Could not create display context:" << error;
438 m_gstGlDisplayContext = QGstContextHandle{
439 gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE,
false),
440 QGstContextHandle::HasRef,
442 gst_context_set_gl_display(m_gstGlDisplayContext.get(), resolvedDisplay->display.get());
444 m_gstGlLocalContext = QGstContextHandle{
445 gst_context_new(
"gst.gl.local_context",
false),
446 QGstContextHandle::HasRef,
449 GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext.get());
450 gst_structure_set(structure,
"context", GST_TYPE_GL_CONTEXT, displayContext.get(),
nullptr);
451 displayContext.reset();
466#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()
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)