diff options
-rw-r--r-- | src/gsttools/gsttools.pro | 2 | ||||
-rw-r--r-- | src/gsttools/qgstreamerplayersession.cpp | 15 | ||||
-rw-r--r-- | src/gsttools/qgstreamervideooverlay.cpp | 9 | ||||
-rw-r--r-- | src/gsttools/qgstreamervideowidget.cpp | 2 | ||||
-rw-r--r-- | src/gsttools/qgstutils.cpp | 8 | ||||
-rw-r--r-- | src/gsttools/qgstutils_p.h | 2 | ||||
-rw-r--r-- | src/gsttools/qgstvideorenderersink.cpp | 185 | ||||
-rw-r--r-- | src/gsttools/qgstvideorenderersink_p.h | 14 | ||||
-rw-r--r-- | src/multimedia/configure.json | 16 | ||||
-rw-r--r-- | src/qtmultimediaquicktools/qsgvideonode_texture.cpp | 1 |
10 files changed, 250 insertions, 4 deletions
diff --git a/src/gsttools/gsttools.pro b/src/gsttools/gsttools.pro index d161fff85..fff039b3b 100644 --- a/src/gsttools/gsttools.pro +++ b/src/gsttools/gsttools.pro @@ -82,6 +82,8 @@ qtConfig(gstreamer_0_10) { qgstvideorenderersink.cpp } +qtConfig(gstreamer_gl): QMAKE_USE += gstreamer_gl + qtConfig(gstreamer_app) { QMAKE_USE += gstreamer_app PRIVATE_HEADERS += qgstappsrc_p.h diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index 9858f61c9..2f6923274 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -206,13 +206,26 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) m_videoOutputBin = gst_bin_new("video-output-bin"); // might not get a parent, take ownership to avoid leak qt_gst_object_ref_sink(GST_OBJECT(m_videoOutputBin)); + + GstElement *videoOutputSink = m_videoIdentity; +#if QT_CONFIG(gstreamer_gl) + if (QGstUtils::useOpenGL()) { + videoOutputSink = gst_element_factory_make("glupload", NULL); + GstElement *colorConvert = gst_element_factory_make("glcolorconvert", NULL); + gst_bin_add_many(GST_BIN(m_videoOutputBin), videoOutputSink, colorConvert, m_videoIdentity, m_nullVideoSink, NULL); + gst_element_link_many(videoOutputSink, colorConvert, m_videoIdentity, NULL); + } else { + gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_nullVideoSink, NULL); + } +#else gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_nullVideoSink, NULL); +#endif gst_element_link(m_videoIdentity, m_nullVideoSink); m_videoSink = m_nullVideoSink; // add ghostpads - GstPad *pad = gst_element_get_static_pad(m_videoIdentity,"sink"); + GstPad *pad = gst_element_get_static_pad(videoOutputSink, "sink"); gst_element_add_pad(GST_ELEMENT(m_videoOutputBin), gst_ghost_pad_new("sink", pad)); gst_object_unref(GST_OBJECT(pad)); diff --git a/src/gsttools/qgstreamervideooverlay.cpp b/src/gsttools/qgstreamervideooverlay.cpp index 1f3e28549..6b862e475 100644 --- a/src/gsttools/qgstreamervideooverlay.cpp +++ b/src/gsttools/qgstreamervideooverlay.cpp @@ -48,6 +48,8 @@ #include <gst/video/videooverlay.h> #endif +#include <QtMultimedia/private/qtmultimediaglobal_p.h> + QT_BEGIN_NAMESPACE struct ElementMap @@ -59,6 +61,9 @@ struct ElementMap // Ordered by descending priority static const ElementMap elementMap[] = { +#if QT_CONFIG(gstreamer_gl) + { "xcb", "glimagesink" }, +#endif { "xcb", "vaapisink" }, { "xcb", "xvimagesink" }, { "xcb", "ximagesink" } @@ -340,6 +345,10 @@ static GstElement *findBestVideoSink() // First, try some known video sinks, depending on the Qt platform plugin in use. for (quint32 i = 0; i < (sizeof(elementMap) / sizeof(ElementMap)); ++i) { +#if QT_CONFIG(gstreamer_gl) + if (!QGstUtils::useOpenGL() && qstrcmp(elementMap[i].gstreamerElement, "glimagesink") == 0) + continue; +#endif if (platform == QLatin1String(elementMap[i].qtPlatform) && (choice = gst_element_factory_make(elementMap[i].gstreamerElement, NULL))) { diff --git a/src/gsttools/qgstreamervideowidget.cpp b/src/gsttools/qgstreamervideowidget.cpp index bb85e1eb7..288a9c9c0 100644 --- a/src/gsttools/qgstreamervideowidget.cpp +++ b/src/gsttools/qgstreamervideowidget.cpp @@ -173,6 +173,8 @@ bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e) } if (e->type() == QEvent::Paint) { + // Update overlay by new size if any. + m_videoOverlay.setRenderRectangle(QRect(0, 0, m_widget->width(), m_widget->height())); if (m_videoOverlay.isActive()) m_videoOverlay.expose(); // triggers a repaint of the last frame else diff --git a/src/gsttools/qgstutils.cpp b/src/gsttools/qgstutils.cpp index 258cbb404..82af0f1e2 100644 --- a/src/gsttools/qgstutils.cpp +++ b/src/gsttools/qgstutils.cpp @@ -1045,11 +1045,13 @@ static const VideoFormat qt_videoFormatLookup[] = { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_BGRx }, { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_RGBx }, { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_BGRA }, + { QVideoFrame::Format_ABGR32, GST_VIDEO_FORMAT_RGBA }, { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_ARGB }, #else { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_xRGB }, { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_xBGR }, { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_ARGB }, + { QVideoFrame::Format_ABGR32, GST_VIDEO_FORMAT_ABGR }, { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_BGRA }, #endif { QVideoFrame::Format_RGB24 , GST_VIDEO_FORMAT_RGB }, @@ -1568,6 +1570,12 @@ QVariant QGstUtils::toGStreamerOrientation(const QVariant &value) } #endif +bool QGstUtils::useOpenGL() +{ + static bool result = qEnvironmentVariableIntValue("QT_GSTREAMER_USE_OPENGL_PLUGIN"); + return result; +} + void qt_gst_object_ref_sink(gpointer object) { #if GST_CHECK_VERSION(0,10,24) diff --git a/src/gsttools/qgstutils_p.h b/src/gsttools/qgstutils_p.h index 387a2e27a..5a2feec17 100644 --- a/src/gsttools/qgstutils_p.h +++ b/src/gsttools/qgstutils_p.h @@ -153,6 +153,8 @@ namespace QGstUtils { Q_GSTTOOLS_EXPORT QVariant fromGStreamerOrientation(const QVariant &value); Q_GSTTOOLS_EXPORT QVariant toGStreamerOrientation(const QVariant &value); #endif + + Q_GSTTOOLS_EXPORT bool useOpenGL(); } Q_GSTTOOLS_EXPORT void qt_gst_object_ref_sink(gpointer object); diff --git a/src/gsttools/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp index 09fdd42a6..49016b952 100644 --- a/src/gsttools/qgstvideorenderersink.cpp +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -54,12 +54,32 @@ #include "qgstutils_p.h" +#if QT_CONFIG(gstreamer_gl) +#include <QOpenGLContext> +#include <QGuiApplication> +#include <QWindow> +#include <qpa/qplatformnativeinterface.h> + +#include <gst/gl/gstglconfig.h> + +#if GST_GL_HAVE_WINDOW_X11 +# include <gst/gl/x11/gstgldisplay_x11.h> +#endif +#if GST_GL_HAVE_PLATFORM_EGL +# include <gst/gl/egl/gstgldisplay_egl.h> +#endif +#if GST_CHECK_VERSION(1,11,1) && GST_GL_HAVE_WINDOW_WAYLAND +# include <gst/gl/wayland/gstgldisplay_wayland.h> +#endif +#endif // #if QT_CONFIG(gstreamer_gl) + //#define DEBUG_VIDEO_SURFACE_SINK QT_BEGIN_NAMESPACE QGstDefaultVideoRenderer::QGstDefaultVideoRenderer() : m_flushed(true) + , m_handleType(QAbstractVideoBuffer::NoHandle) { } @@ -69,13 +89,33 @@ QGstDefaultVideoRenderer::~QGstDefaultVideoRenderer() GstCaps *QGstDefaultVideoRenderer::getCaps(QAbstractVideoSurface *surface) { - return QGstUtils::capsForFormats(surface->supportedPixelFormats()); +#if QT_CONFIG(gstreamer_gl) + if (QGstUtils::useOpenGL()) { + m_handleType = QAbstractVideoBuffer::GLTextureHandle; + auto formats = surface->supportedPixelFormats(m_handleType); + // Even if the surface does not support gl textures, + // glupload will be added to the pipeline and GLMemory will be requested. + // This will lead to upload data to gl textures + // and download it when the buffer will be used within rendering. + if (formats.isEmpty()) { + m_handleType = QAbstractVideoBuffer::NoHandle; + formats = surface->supportedPixelFormats(m_handleType); + } + + GstCaps *caps = QGstUtils::capsForFormats(formats); + for (guint i = 0; i < gst_caps_get_size(caps); ++i) + gst_caps_set_features(caps, i, gst_caps_features_from_string("memory:GLMemory")); + + return caps; + } +#endif + return QGstUtils::capsForFormats(surface->supportedPixelFormats(QAbstractVideoBuffer::NoHandle)); } bool QGstDefaultVideoRenderer::start(QAbstractVideoSurface *surface, GstCaps *caps) { m_flushed = true; - m_format = QGstUtils::formatForCaps(caps, &m_videoInfo); + m_format = QGstUtils::formatForCaps(caps, &m_videoInfo, m_handleType); return m_format.isValid() && surface->start(m_format); } @@ -90,8 +130,21 @@ void QGstDefaultVideoRenderer::stop(QAbstractVideoSurface *surface) bool QGstDefaultVideoRenderer::present(QAbstractVideoSurface *surface, GstBuffer *buffer) { m_flushed = false; + + QGstVideoBuffer *videoBuffer = nullptr; +#if QT_CONFIG(gstreamer_gl) + if (m_format.handleType() == QAbstractVideoBuffer::GLTextureHandle) { + GstGLMemory *glmem = GST_GL_MEMORY_CAST(gst_buffer_peek_memory(buffer, 0)); + guint textureId = gst_gl_memory_get_texture_id(glmem); + videoBuffer = new QGstVideoBuffer(buffer, m_videoInfo, m_format.handleType(), textureId); + } +#endif + + if (!videoBuffer) + videoBuffer = new QGstVideoBuffer(buffer, m_videoInfo); + QVideoFrame frame( - new QGstVideoBuffer(buffer, m_videoInfo), + videoBuffer, m_format.frameSize(), m_format.pixelFormat()); QGstUtils::setFrameTimeStamps(&frame, buffer); @@ -145,6 +198,10 @@ QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate() gst_caps_unref(m_surfaceCaps); if (m_startCaps) gst_caps_unref(m_startCaps); +#if QT_CONFIG(gstreamer_gl) + if (m_gstGLDisplayContext) + gst_object_unref(m_gstGLDisplayContext); +#endif } GstCaps *QVideoSurfaceGstDelegate::caps() @@ -254,6 +311,118 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) return m_renderReturn; } +#if QT_CONFIG(gstreamer_gl) +static GstGLContext *gstGLDisplayContext(QAbstractVideoSurface *surface) +{ + QOpenGLContext *glContext = qobject_cast<QOpenGLContext*>(surface->property("GLContext").value<QObject*>()); + // Context is not ready yet. + if (!glContext) + return nullptr; + + GstGLDisplay *display = nullptr; + const QString platform = QGuiApplication::platformName(); + const char *contextName = "eglcontext"; + GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL; + QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface(); + +#if GST_GL_HAVE_WINDOW_X11 + if (platform == QLatin1String("xcb")) { + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + contextName = "glxcontext"; + glPlatform = GST_GL_PLATFORM_GLX; + } + + display = (GstGLDisplay *)gst_gl_display_x11_new_with_display( + (Display *)pni->nativeResourceForIntegration("display")); + } +#endif + +#if GST_GL_HAVE_PLATFORM_EGL + if (!display && platform == QLatin1String("eglfs")) { + display = (GstGLDisplay *)gst_gl_display_egl_new_with_egl_display( + pni->nativeResourceForIntegration("egldisplay")); + } +#endif + +#if GST_CHECK_VERSION(1,11,1) +#if GST_GL_HAVE_WINDOW_WAYLAND + if (!display && platform.startsWith(QLatin1String("wayland"))) { + const char *displayName = (platform == QLatin1String("wayland")) + ? "display" : "egldisplay"; + + display = (GstGLDisplay *)gst_gl_display_wayland_new_with_display( + (struct wl_display *)pni->nativeResourceForIntegration(displayName)); + } +#endif +#endif + + if (!display) { + qWarning() << "Could not create GstGLDisplay"; + return nullptr; + } + + void *nativeContext = pni->nativeResourceForContext(contextName, glContext); + if (!nativeContext) + qWarning() << "Could not find resource for" << contextName; + + GstGLContext *appContext = gst_gl_context_new_wrapped(display, (guintptr)nativeContext, glPlatform, GST_GL_API_ANY); + if (!appContext) + qWarning() << "Could not create wrappped context for platform:" << glPlatform; + + GstGLContext *displayContext = nullptr; + GError *error = NULL; + gst_gl_display_create_context(display, appContext, &displayContext, &error); + if (error) { + qWarning() << "Could not create display context:" << error->message; + g_clear_error(&error); + } + + if (appContext) + gst_object_unref(appContext); + + gst_object_unref(display); + + return displayContext; +} +#endif // #if QT_CONFIG(gstreamer_gl) + +bool QVideoSurfaceGstDelegate::query(GstQuery *query) +{ +#if QT_CONFIG(gstreamer_gl) + if (GST_QUERY_TYPE(query) == GST_QUERY_CONTEXT) { + const gchar *type; + gst_query_parse_context_type(query, &type); + + if (strcmp(type, "gst.gl.local_context") != 0) + return false; + + if (!m_gstGLDisplayContext) + m_gstGLDisplayContext = gstGLDisplayContext(m_surface); + + // No context yet. + if (!m_gstGLDisplayContext) + return false; + + GstContext *context = NULL; + gst_query_parse_context(query, &context); + context = context ? gst_context_copy(context) : gst_context_new(type, FALSE); + GstStructure *structure = gst_context_writable_structure(context); +#if GST_CHECK_VERSION(1,11,1) + gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, m_gstGLDisplayContext, NULL); +#else + gst_structure_set(structure, "context", GST_GL_TYPE_CONTEXT, m_gstGLDisplayContext, NULL); +#endif + gst_query_set_context(query, context); + gst_context_unref(context); + + return m_gstGLDisplayContext; + } +#else + Q_UNUSED(query); +#endif + return false; +} + bool QVideoSurfaceGstDelegate::event(QEvent *event) { if (event->type() == QEvent::UpdateRequest) { @@ -460,6 +629,7 @@ void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data) base_sink_class->propose_allocation = QGstVideoRendererSink::propose_allocation; base_sink_class->stop = QGstVideoRendererSink::stop; base_sink_class->unlock = QGstVideoRendererSink::unlock; + base_sink_class->query = QGstVideoRendererSink::query; GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class); element_class->change_state = QGstVideoRendererSink::change_state; @@ -602,4 +772,13 @@ GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *base, GstBuffer *b return sink->delegate->render(buffer); } +gboolean QGstVideoRendererSink::query(GstBaseSink *base, GstQuery *query) +{ + VO_SINK(base); + if (sink->delegate->query(query)) + return TRUE; + + return GST_BASE_SINK_CLASS(sink_parent_class)->query(base, query); +} + QT_END_NAMESPACE diff --git a/src/gsttools/qgstvideorenderersink_p.h b/src/gsttools/qgstvideorenderersink_p.h index d2417a7c9..38854291a 100644 --- a/src/gsttools/qgstvideorenderersink_p.h +++ b/src/gsttools/qgstvideorenderersink_p.h @@ -51,6 +51,7 @@ // We mean it. // +#include <QtMultimedia/private/qtmultimediaglobal_p.h> #include <gst/video/gstvideosink.h> #include <gst/video/video.h> @@ -67,6 +68,13 @@ #include "qgstvideorendererplugin_p.h" +#if QT_CONFIG(gstreamer_gl) +#ifndef GST_USE_UNSTABLE_API +#define GST_USE_UNSTABLE_API +#endif +#include <gst/gl/gl.h> +#endif + QT_BEGIN_NAMESPACE class QAbstractVideoSurface; @@ -89,6 +97,7 @@ private: QVideoSurfaceFormat m_format; GstVideoInfo m_videoInfo; bool m_flushed; + QAbstractVideoBuffer::HandleType m_handleType; }; class QVideoSurfaceGstDelegate : public QObject @@ -110,6 +119,7 @@ public: GstFlowReturn render(GstBuffer *buffer); bool event(QEvent *event) override; + bool query(GstQuery *query); private slots: bool handleEvent(QMutexLocker *locker); @@ -132,6 +142,9 @@ private: GstCaps *m_surfaceCaps; GstCaps *m_startCaps; GstBuffer *m_renderBuffer; +#if QT_CONFIG(gstreamer_gl) + GstGLContext *m_gstGLDisplayContext = nullptr; +#endif bool m_notified; bool m_stop; @@ -168,6 +181,7 @@ private: static gboolean unlock(GstBaseSink *sink); static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer); + static gboolean query(GstBaseSink *element, GstQuery *query); private: QVideoSurfaceGstDelegate *delegate; diff --git a/src/multimedia/configure.json b/src/multimedia/configure.json index 6d56af5ed..ca2839cea 100644 --- a/src/multimedia/configure.json +++ b/src/multimedia/configure.json @@ -95,6 +95,17 @@ { "libs": "-lgstphotography-1.0" } ] }, + "gstreamer_gl_1_0": { + "label": "GStreamer OpenGL 1.0", + "export": "gstreamer_gl", + "test": { + "include": "gst/gl/gl.h" + }, + "use": "gstreamer_1_0", + "sources": [ + { "type": "pkgConfig", "args": "gstreamer-gl-1.0" } + ] + }, "libresourceqt5": { "label": "libresourceqt5", "test": "resourcepolicy", @@ -229,6 +240,11 @@ "condition": "(features.gstreamer_1_0 && libs.gstreamer_photography_1_0) || (features.gstreamer_0_10 && libs.gstreamer_photography_0_10)", "output": [ "privateFeature" ] }, + "gstreamer_gl": { + "label": "GStreamer OpenGL", + "condition": "features.gstreamer_1_0 && libs.gstreamer_gl_1_0", + "output": [ "privateFeature" ] + }, "gpu_vivante": { "label": "Vivante GPU", "condition": "features.gui && features.opengles2 && tests.gpu_vivante", diff --git a/src/qtmultimediaquicktools/qsgvideonode_texture.cpp b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp index f5545afc7..318e4cef5 100644 --- a/src/qtmultimediaquicktools/qsgvideonode_texture.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp @@ -58,6 +58,7 @@ QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_Texture::supportedPixelForma pixelFormats.append(QVideoFrame::Format_ARGB32); pixelFormats.append(QVideoFrame::Format_BGR32); pixelFormats.append(QVideoFrame::Format_BGRA32); + pixelFormats.append(QVideoFrame::Format_ABGR32); } return pixelFormats; |