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 <common/qgstreameregldisplay_p.h>
26# include <gst/gl/egl/gstgldisplay_egl.h>
28# include <EGL/eglext.h>
30# if QT_CONFIG(gstreamer_gl_wayland)
31# include <gst/gl/wayland/gstgldisplay_wayland.h>
37Q_STATIC_LOGGING_CATEGORY(qLcGstVideoSink,
"qt.multimedia.gstvideosink");
40 : QPlatformVideoSink{ parent }
73 QGstElementFactoryHandle factory;
77 QByteArray preprocessOverride = qgetenv(
"QT_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT");
78 if (!preprocessOverride.isEmpty()) {
79 qCDebug(qLcGstVideoSink) <<
"requesting conversion element from environment:"
80 << preprocessOverride;
82 m_gstPreprocess = QGstBin::createFromPipelineDescription(preprocessOverride,
nullptr,
85 qCWarning(qLcGstVideoSink) <<
"Cannot create conversion element:" << preprocessOverride;
88 if (!m_gstPreprocess) {
93 static constexpr auto decodersToTest = {
94 "imxvideoconvert_g2d",
98 for (
const char *decoder : decodersToTest) {
99 factory = QGstElement::findFactory(decoder);
105 qCDebug(qLcGstVideoSink)
106 <<
"instantiating conversion element:"
107 << g_type_name(gst_element_factory_get_element_type(factory.get()));
109 m_gstPreprocess = QGstElement::createFromFactory(factory,
"preprocess");
113 bool disablePixelAspectRatio =
114 qEnvironmentVariableIntValue(
"QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO") != 0;
115 if (disablePixelAspectRatio) {
123 QGstElement::createFromFactory(
"identity",
"nullPixelAspectRatioCapsFilter");
126 QGstElement::createFromFactory(
"capsfilter",
"pixelAspectRatioCapsFilter");
128 gst_caps_new_simple(
"video/x-raw",
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL),
131 g_object_set(m_gstCapsFilter.element(),
"caps", capsFilterCaps.caps(), NULL);
134 if (m_gstPreprocess) {
135 m_sinkBin.add(m_gstPreprocess, m_gstCapsFilter);
136 qLinkGstElements(m_gstPreprocess, m_gstCapsFilter);
137 m_sinkBin.addGhostPad(m_gstPreprocess,
"sink");
139 m_sinkBin.add(m_gstCapsFilter);
140 m_sinkBin.addGhostPad(m_gstCapsFilter,
"sink");
148 emit aboutToBeDestroyed();
155 if (!m_gstVideoSink) {
159 updateSinkElement(m_gstQtSink);
167 if (m_isActive == isActive)
169 m_isActive = isActive;
172 m_gstQtSink.setActive(isActive);
177 m_sinkIsAsync = isAsync;
182 Q_ASSERT(pluggableSink);
183 m_pluggableVideoSink = pluggableSink;
184 m_pluggableVideoSink->setVideoFrame(m_currentVideoFrame);
185 m_pluggableVideoSink->setSubtitleText(m_currentSubtitleText);
186 m_pluggableVideoSink->setNativeSize(m_currentNativeSize);
187 connect(
this, &QGstreamerRelayVideoSink::videoFrameChanged,
188 m_pluggableVideoSink, &QPlatformVideoSink::setVideoFrame);
189 connect(
this, &QGstreamerRelayVideoSink::subtitleTextChanged,
190 m_pluggableVideoSink, &QPlatformVideoSink::setSubtitleText);
192 m_pluggableVideoSink, &QPlatformVideoSink::setNativeSize);
195 renderingRhiChanged(m_pluggableVideoSink->rhi());
196 m_rhiConnection = connect(m_pluggableVideoSink, &QPlatformVideoSink::rhiChanged,
this, [
this](){
197 renderingRhiChanged(m_pluggableVideoSink->rhi());
203 if (m_pluggableVideoSink) {
204 disconnect(
this, &QGstreamerRelayVideoSink::videoFrameChanged,
205 m_pluggableVideoSink, &QPlatformVideoSink::setVideoFrame);
206 disconnect(
this, &QGstreamerRelayVideoSink::subtitleTextChanged,
207 m_pluggableVideoSink, &QPlatformVideoSink::setSubtitleText);
209 m_pluggableVideoSink, &QPlatformVideoSink::setNativeSize);
210 disconnect(m_rhiConnection);
211 m_pluggableVideoSink =
nullptr;
217 emit videoFrameChanged(frame);
218 m_currentVideoFrame = frame;
223 emit subtitleTextChanged(subtitleText);
224 m_currentSubtitleText = subtitleText;
229 emit nativeSizeChanged(size);
230 m_currentNativeSize = size;
235 Q_ASSERT(!m_gstQtSink);
237 m_gstQtSink = QGstVideoRendererSink::createSink(
this);
239 m_gstQtSink.set(
"async",
false);
240 m_gstQtSink.setActive(m_isActive);
245 if (newSink == m_gstVideoSink)
248 m_gstCapsFilter.src().modifyPipelineInIdleProbe([&] {
250 m_sinkBin.stopAndRemoveElements(m_gstVideoSink);
252 m_gstVideoSink = std::move(newSink);
253 m_sinkBin.add(m_gstVideoSink);
254 qLinkGstElements(m_gstCapsFilter, m_gstVideoSink);
255 GstEvent *event = gst_event_new_reconfigure();
256 gst_element_send_event(m_gstVideoSink.element(), event);
257 m_gstVideoSink.syncStateWithParent();
260 m_sinkBin.dumpPipelineGraph(
"updateSinkElement");
265 m_gstGlDisplayContext.reset();
266 m_gstGlLocalContext.reset();
271 QRhi *currentRhi = rhi();
273 using namespace Qt::Literals;
277#if QT_CONFIG(gstreamer_gl)
278 if (!currentRhi || currentRhi->backend() != QRhi::OpenGLES2)
280 auto *nativeHandles =
static_cast<
const QRhiGles2NativeHandles *>(currentRhi->nativeHandles());
281 auto glContext = nativeHandles->context;
284 const QString platform = QGuiApplication::platformName();
285 QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface();
287 QGstGLDisplayHandle gstGlDisplay;
289 QByteArray contextName =
"eglcontext"_ba;
290 GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL;
292# if QT_CONFIG(gstreamer_gl_egl)
293 if (
auto eglDisplay = qGstEglDisplay()) {
294 gstGlDisplay.reset(GST_GL_DISPLAY_CAST(gst_gl_display_egl_new_with_egl_display(eglDisplay)),
295 QGstGLDisplayHandle::HasRef);
299 auto display = pni->nativeResourceForIntegration(
"display"_ba);
302# if QT_CONFIG(gstreamer_gl_x11)
303 if (platform == QLatin1String(
"xcb")) {
304 contextName =
"glxcontext"_ba;
305 glPlatform = GST_GL_PLATFORM_GLX;
307 gstGlDisplay.reset(GST_GL_DISPLAY_CAST(gst_gl_display_x11_new_with_display(
308 reinterpret_cast<Display *>(display))),
309 QGstGLDisplayHandle::HasRef);
312# if QT_CONFIG(gstreamer_gl_wayland)
313 if (platform.startsWith(QLatin1String(
"wayland"))) {
314 Q_ASSERT(!gstGlDisplay);
315 gstGlDisplay.reset(GST_GL_DISPLAY_CAST(gst_gl_display_wayland_new_with_display(
316 reinterpret_cast<
struct wl_display *>(display))),
317 QGstGLDisplayHandle::HasRef);
324 qWarning() <<
"Could not create GstGLDisplay";
328 void *nativeContext = pni->nativeResourceForContext(contextName, glContext);
330 qWarning() <<
"Could not find resource for" << contextName;
332 GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2;
333 QGstGLContextHandle appContext{
334 gst_gl_context_new_wrapped(gstGlDisplay.get(), guintptr(nativeContext), glPlatform, glApi),
335 QGstGLContextHandle::HasRef,
338 qWarning() <<
"Could not create wrappped context for platform:" << glPlatform;
340 gst_gl_context_activate(appContext.get(),
true);
342 QUniqueGErrorHandle error;
343 gst_gl_context_fill_info(appContext.get(), &error);
345 qWarning() <<
"Could not fill context info:" << error;
349 QGstGLContextHandle displayContext;
350 gst_gl_display_create_context(gstGlDisplay.get(), appContext.get(), &displayContext, &error);
352 qWarning() <<
"Could not create display context:" << error;
356 m_gstGlDisplayContext.reset(gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE,
false),
357 QGstContextHandle::HasRef);
358 gst_context_set_gl_display(m_gstGlDisplayContext.get(), gstGlDisplay.get());
360 m_gstGlLocalContext.reset(gst_context_new(
"gst.gl.local_context",
false),
361 QGstContextHandle::HasRef);
362 GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext.get());
363 gst_structure_set(structure,
"context", GST_TYPE_GL_CONTEXT, displayContext.get(),
nullptr);
364 displayContext.reset();
379#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.