summaryrefslogtreecommitdiffstats
path: root/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp')
-rw-r--r--src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp236
1 files changed, 132 insertions, 104 deletions
diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
index dc439f71e..6467673d7 100644
--- a/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
+++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideosink.cpp
@@ -1,50 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qgstreamervideosink_p.h"
-#include "qgstvideorenderersink_p.h"
-#include "qgstsubtitlesink_p.h"
-#include <qgstutils_p.h>
-#include <QtGui/private/qrhi_p.h>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <common/qgstreamervideosink_p.h>
+#include <common/qgstvideorenderersink_p.h>
+#include <common/qgstsubtitlesink_p.h>
+#include <common/qgst_debug_p.h>
+#include <common/qgstutils_p.h>
+#include <rhi/qrhi.h>
#if QT_CONFIG(gstreamer_gl)
-#include <QtGui/private/qrhigles2_p.h>
#include <QGuiApplication>
#include <QtGui/qopenglcontext.h>
#include <QWindow>
@@ -70,34 +34,86 @@
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(qLcMediaVideoSink, "qt.multimedia.videosink")
+static Q_LOGGING_CATEGORY(qLcGstVideoSink, "qt.multimedia.gstvideosink");
QGstreamerVideoSink::QGstreamerVideoSink(QVideoSink *parent)
- : QPlatformVideoSink(parent)
+ : QPlatformVideoSink{
+ parent,
+ },
+ sinkBin{
+ QGstBin::create("videoSinkBin"),
+ }
{
- sinkBin = QGstBin("videoSinkBin");
- // This is a hack for some iMX platforms. Thos require the use of a special video
+ // This is a hack for some iMX and NVidia platforms. These require the use of a special video
// conversion element in the pipeline before the video sink, as they unfortunately
- // output some proprietary format from the decoder even though it's marked as
+ // output some proprietary format from the decoder even though it's sometimes marked as
// a regular supported video/x-raw format.
//
// To fix this, simply insert the element into the pipeline if it's available. Otherwise
// we simply use an identity element.
- gstQueue = QGstElement("queue");
- auto imxVideoConvert = QGstElement("imxvideoconvert_g2d");
- if (!imxVideoConvert.isNull())
- gstPreprocess = imxVideoConvert;
- else
- gstPreprocess = QGstElement("identity");
- sinkBin.add(gstQueue, gstPreprocess);
- gstQueue.link(gstPreprocess);
- sinkBin.addGhostPad(gstQueue, "sink");
-
- gstSubtitleSink = GST_ELEMENT(QGstSubtitleSink::createSink(this));
+ QGstElementFactoryHandle factory;
+
+ // QT_MULTIMEDIA_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT allows users to override the
+ // conversion element. Ideally we construct the element programatically, though.
+ QByteArray preprocessOverride =
+ qgetenv("QT_MULTIMEDIA_GSTREAMER_OVERRIDE_VIDEO_CONVERSION_ELEMENT");
+ if (!preprocessOverride.isEmpty()) {
+ qCDebug(qLcGstVideoSink) << "requesting conversion element from environment: "
+ << preprocessOverride;
+ factory = QGstElement::findFactory(preprocessOverride);
+ }
+
+ if (!factory)
+ factory = QGstElement::findFactory("imxvideoconvert_g2d");
+
+ if (!factory)
+ factory = QGstElement::findFactory("nvvidconv");
+
+ if (factory) {
+ qCDebug(qLcGstVideoSink) << "instantiating conversion element: "
+ << g_type_name(
+ gst_element_factory_get_element_type(factory.get()));
+
+ gstPreprocess = QGstElement::createFromFactory(factory, "preprocess");
+ }
+
+ bool disablePixelAspectRatio =
+ qEnvironmentVariableIsSet("QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO");
+ if (disablePixelAspectRatio) {
+ // Enabling the pixel aspect ratio may expose a gstreamer bug on cameras that don't expose a
+ // pixel-aspect-ratio via `VIDIOC_CROPCAP`. This can cause the caps negotiation to fail.
+ // Using the QT_GSTREAMER_DISABLE_PIXEL_ASPECT_RATIO environment variable, one can disable
+ // pixel-aspect-ratio handling
+ //
+ // compare: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6242
+ gstCapsFilter =
+ QGstElement::createFromFactory("identity", "nullPixelAspectRatioCapsFilter");
+ } else {
+ gstCapsFilter = QGstElement::createFromFactory("capsfilter", "pixelAspectRatioCapsFilter");
+ QGstCaps capsFilterCaps{
+ gst_caps_new_simple("video/x-raw", "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL),
+ QGstCaps::HasRef,
+ };
+ g_object_set(gstCapsFilter.element(), "caps", capsFilterCaps.caps(), NULL);
+ }
+
+ if (gstPreprocess) {
+ sinkBin.add(gstPreprocess, gstCapsFilter);
+ qLinkGstElements(gstPreprocess, gstCapsFilter);
+ sinkBin.addGhostPad(gstPreprocess, "sink");
+ } else {
+ sinkBin.add(gstCapsFilter);
+ sinkBin.addGhostPad(gstCapsFilter, "sink");
+ }
+
+ gstSubtitleSink =
+ QGstElement(GST_ELEMENT(QGstSubtitleSink::createSink(this)), QGstElement::NeedsRef);
}
QGstreamerVideoSink::~QGstreamerVideoSink()
{
+ emit aboutToBeDestroyed();
+
unrefGstContexts();
setPipeline(QGstPipeline());
@@ -111,7 +127,7 @@ QGstElement QGstreamerVideoSink::gstSink()
void QGstreamerVideoSink::setPipeline(QGstPipeline pipeline)
{
- gstPipeline = pipeline;
+ gstPipeline = std::move(pipeline);
}
bool QGstreamerVideoSink::inStoppedState() const
@@ -139,7 +155,11 @@ void QGstreamerVideoSink::setRhi(QRhi *rhi)
void QGstreamerVideoSink::createQtSink()
{
- gstQtSink = QGstElement(reinterpret_cast<GstElement *>(QGstVideoRendererSink::createSink(this)));
+ if (gstQtSink)
+ gstQtSink.setStateSync(GST_STATE_NULL);
+
+ gstQtSink = QGstElement(reinterpret_cast<GstElement *>(QGstVideoRendererSink::createSink(this)),
+ QGstElement::NeedsRef);
}
void QGstreamerVideoSink::updateSinkElement()
@@ -152,37 +172,33 @@ void QGstreamerVideoSink::updateSinkElement()
if (newSink == gstVideoSink)
return;
- gstPipeline.beginConfig();
+ gstPipeline.modifyPipelineWhileNotRunning([&] {
+ if (!gstVideoSink.isNull())
+ sinkBin.stopAndRemoveElements(gstVideoSink);
- if (!gstVideoSink.isNull()) {
- gstVideoSink.setStateSync(GST_STATE_NULL);
- sinkBin.remove(gstVideoSink);
- }
+ newSink.set("async", false); // no asynchronous state changes
- gstVideoSink = newSink;
- sinkBin.add(gstVideoSink);
- if (!gstPreprocess.link(gstVideoSink))
- qCDebug(qLcMediaVideoSink) << "couldn't link preprocess and sink";
- gstVideoSink.setState(GST_STATE_PAUSED);
+ gstVideoSink = newSink;
+ sinkBin.add(gstVideoSink);
+ qLinkGstElements(gstCapsFilter, gstVideoSink);
+ gstVideoSink.setState(GST_STATE_PAUSED);
+ });
- gstPipeline.endConfig();
gstPipeline.dumpGraph("updateVideoSink");
}
void QGstreamerVideoSink::unrefGstContexts()
{
- if (m_gstGlDisplayContext)
- gst_context_unref(m_gstGlDisplayContext);
- m_gstGlDisplayContext = nullptr;
- if (m_gstGlLocalContext)
- gst_context_unref(m_gstGlLocalContext);
- m_gstGlLocalContext = nullptr;
+ m_gstGlDisplayContext.close();
+ m_gstGlLocalContext.close();
m_eglDisplay = nullptr;
m_eglImageTargetTexture2D = nullptr;
}
void QGstreamerVideoSink::updateGstContexts()
{
+ using namespace Qt::Literals;
+
unrefGstContexts();
#if QT_CONFIG(gstreamer_gl)
@@ -195,34 +211,38 @@ void QGstreamerVideoSink::updateGstContexts()
const QString platform = QGuiApplication::platformName();
QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface();
- m_eglDisplay = pni->nativeResourceForIntegration("egldisplay");
+ m_eglDisplay = pni->nativeResourceForIntegration("egldisplay"_ba);
// qDebug() << "platform is" << platform << m_eglDisplay;
- GstGLDisplay *gstGlDisplay = nullptr;
- const char *contextName = "eglcontext";
+ QGstGLDisplayHandle gstGlDisplay;
+
+ QByteArray contextName = "eglcontext"_ba;
GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL;
// use the egl display if we have one
if (m_eglDisplay) {
#if GST_GL_HAVE_PLATFORM_EGL
- gstGlDisplay = (GstGLDisplay *)gst_gl_display_egl_new_with_egl_display(m_eglDisplay);
+ gstGlDisplay.reset(
+ GST_GL_DISPLAY_CAST(gst_gl_display_egl_new_with_egl_display(m_eglDisplay)));
m_eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES");
#endif
} else {
- auto display = pni->nativeResourceForIntegration("display");
+ auto display = pni->nativeResourceForIntegration("display"_ba);
if (display) {
#if GST_GL_HAVE_WINDOW_X11 && __has_include("X11/Xlib-xcb.h")
if (platform == QLatin1String("xcb")) {
- contextName = "glxcontext";
+ contextName = "glxcontext"_ba;
glPlatform = GST_GL_PLATFORM_GLX;
- gstGlDisplay = (GstGLDisplay *)gst_gl_display_x11_new_with_display((Display *)display);
+ gstGlDisplay.reset(GST_GL_DISPLAY_CAST(
+ gst_gl_display_x11_new_with_display(reinterpret_cast<Display *>(display))));
}
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && __has_include("wayland-client.h")
if (platform.startsWith(QLatin1String("wayland"))) {
Q_ASSERT(!gstGlDisplay);
- gstGlDisplay = (GstGLDisplay *)gst_gl_display_wayland_new_with_display((struct wl_display *)display);
+ gstGlDisplay.reset(GST_GL_DISPLAY_CAST(gst_gl_display_wayland_new_with_display(
+ reinterpret_cast<struct wl_display *>(display))));
}
#endif
}
@@ -238,33 +258,41 @@ void QGstreamerVideoSink::updateGstContexts()
qWarning() << "Could not find resource for" << contextName;
GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2;
- GstGLContext *appContext = gst_gl_context_new_wrapped(gstGlDisplay, (guintptr)nativeContext, glPlatform, glApi);
+ QGstGLContextHandle appContext{
+ gst_gl_context_new_wrapped(gstGlDisplay.get(), guintptr(nativeContext), glPlatform, glApi),
+ };
if (!appContext)
qWarning() << "Could not create wrappped context for platform:" << glPlatform;
- GstGLContext *displayContext = nullptr;
- GError *error = nullptr;
- gst_gl_display_create_context(gstGlDisplay, appContext, &displayContext, &error);
+ gst_gl_context_activate(appContext.get(), true);
+
+ QUniqueGErrorHandle error;
+ gst_gl_context_fill_info(appContext.get(), &error);
if (error) {
- qWarning() << "Could not create display context:" << error->message;
- g_clear_error(&error);
+ qWarning() << "Could not fill context info:" << error;
+ error = {};
}
- if (appContext)
- gst_object_unref(appContext);
+ QGstGLContextHandle displayContext;
+ gst_gl_display_create_context(gstGlDisplay.get(), appContext.get(), &displayContext, &error);
+ if (error)
+ qWarning() << "Could not create display context:" << error;
- m_gstGlDisplayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, false);
- gst_context_set_gl_display(m_gstGlDisplayContext, gstGlDisplay);
- gst_object_unref(gstGlDisplay);
+ appContext.close();
- m_gstGlLocalContext = gst_context_new("gst.gl.local_context", false);
- GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext);
- gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, displayContext, nullptr);
- gst_object_unref(displayContext);
+ m_gstGlDisplayContext.reset(gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, false));
+ gst_context_set_gl_display(m_gstGlDisplayContext.get(), gstGlDisplay.get());
+
+ m_gstGlLocalContext.reset(gst_context_new("gst.gl.local_context", false));
+ GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext.get());
+ gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, displayContext.get(), nullptr);
+ displayContext.close();
if (!gstPipeline.isNull())
- gst_element_set_context(gstPipeline.element(), m_gstGlLocalContext);
+ gst_element_set_context(gstPipeline.element(), m_gstGlLocalContext.get());
#endif // #if QT_CONFIG(gstreamer_gl)
}
QT_END_NAMESPACE
+
+#include "moc_qgstreamervideosink_p.cpp"