diff options
Diffstat (limited to 'src/gsttools')
-rw-r--r-- | src/gsttools/gsttools.pro | 18 | ||||
-rw-r--r-- | src/gsttools/qgstappsrc.cpp | 4 | ||||
-rw-r--r-- | src/gsttools/qgstreamermirtexturerenderer.cpp | 357 | ||||
-rw-r--r-- | src/gsttools/qgstreamermirtexturerenderer_p.h | 119 | ||||
-rw-r--r-- | src/gsttools/qgstreamerplayercontrol.cpp | 8 | ||||
-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 | 16 | ||||
-rw-r--r-- | src/gsttools/qgstutils_p.h | 2 | ||||
-rw-r--r-- | src/gsttools/qgstvideorenderersink.cpp | 184 | ||||
-rw-r--r-- | src/gsttools/qgstvideorenderersink_p.h | 14 |
12 files changed, 248 insertions, 500 deletions
diff --git a/src/gsttools/gsttools.pro b/src/gsttools/gsttools.pro index b13479ce7..0127cbe92 100644 --- a/src/gsttools/gsttools.pro +++ b/src/gsttools/gsttools.pro @@ -82,14 +82,7 @@ qtConfig(gstreamer_0_10) { qgstvideorenderersink.cpp } -qtConfig(mirclient): { - qtConfig(opengles2):qtHaveModule(widgets) { - PRIVATE_HEADERS += qgstreamermirtexturerenderer_p.h - SOURCES += qgstreamermirtexturerenderer.cpp - QT += opengl quick - LIBS += -lEGL - } -} +qtConfig(gstreamer_gl): QMAKE_USE += gstreamer_gl qtConfig(gstreamer_app) { QMAKE_USE += gstreamer_app @@ -97,6 +90,15 @@ qtConfig(gstreamer_app) { SOURCES += qgstappsrc.cpp } +android { + LIBS_PRIVATE += \ + -L$$(GSTREAMER_ROOT_ANDROID)/armv7/lib \ + -Wl,--whole-archive \ + -lgstapp-1.0 -lgstreamer-1.0 -lgstaudio-1.0 -lgsttag-1.0 -lgstvideo-1.0 -lgstbase-1.0 -lgstpbutils-1.0 \ + -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lffi -lintl -liconv -lorc-0.4 \ + -Wl,--no-whole-archive +} + HEADERS += $$PRIVATE_HEADERS load(qt_module) diff --git a/src/gsttools/qgstappsrc.cpp b/src/gsttools/qgstappsrc.cpp index 3e8b8b9b3..d5c44ec08 100644 --- a/src/gsttools/qgstappsrc.cpp +++ b/src/gsttools/qgstappsrc.cpp @@ -187,10 +187,10 @@ void QGstAppSrc::pushDataToAppSrc() } #endif } - } else { + } else if (!m_sequential) { sendEOS(); } - } else if (m_stream->atEnd()) { + } else if (m_stream->atEnd() && !m_sequential) { sendEOS(); } } diff --git a/src/gsttools/qgstreamermirtexturerenderer.cpp b/src/gsttools/qgstreamermirtexturerenderer.cpp deleted file mode 100644 index 35050db03..000000000 --- a/src/gsttools/qgstreamermirtexturerenderer.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Canonical 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 "qgstreamermirtexturerenderer_p.h" - -#include <qgstreamerplayersession.h> -#include <private/qvideosurfacegstsink_p.h> -#include <private/qgstutils_p.h> -#include <qabstractvideosurface.h> - -#include <QAbstractVideoBuffer> -#include <QGuiApplication> -#include <QDebug> -#include <QtQuick/QQuickWindow> -#include <QOpenGLContext> -#include <QGLContext> -#include <QGuiApplication> -#include <qgl.h> - -#include <gst/gst.h> - -static QGstreamerMirTextureRenderer *rendererInstance = NULL; - -class QGstreamerMirTextureBuffer : public QAbstractVideoBuffer -{ -public: - QGstreamerMirTextureBuffer(GLuint textureId) : - QAbstractVideoBuffer(QAbstractVideoBuffer::GLTextureHandle), - m_textureId(textureId) - { - } - - MapMode mapMode() const { return NotMapped; } - - uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) - { - qDebug() << Q_FUNC_INFO; - Q_UNUSED(mode); - Q_UNUSED(numBytes); - Q_UNUSED(bytesPerLine); - - return NULL; - } - - void unmap() { qDebug() << Q_FUNC_INFO; } - - QVariant handle() const { return QVariant::fromValue<unsigned int>(m_textureId); } - - GLuint textureId() { return m_textureId; } - -private: - GLuint m_textureId; -}; - -QGstreamerMirTextureRenderer::QGstreamerMirTextureRenderer(QObject *parent - , const QGstreamerPlayerSession *playerSession) - : QVideoRendererControl(0), m_videoSink(0), m_surface(0), - m_glSurface(0), - m_context(0), - m_glContext(0), - m_textureId(0), - m_offscreenSurface(0), - m_textureBuffer(0) -{ - Q_UNUSED(parent); - setPlayerSession(playerSession); -} - -QGstreamerMirTextureRenderer::~QGstreamerMirTextureRenderer() -{ - if (m_videoSink) - gst_object_unref(GST_OBJECT(m_videoSink)); - - delete m_glContext; - delete m_offscreenSurface; -} - -GstElement *QGstreamerMirTextureRenderer::videoSink() -{ - qDebug() << Q_FUNC_INFO; - - // FIXME: Ugly hack until I figure out why passing this segfaults in the g_signal handler - rendererInstance = const_cast<QGstreamerMirTextureRenderer*>(this); - - if (!m_videoSink && m_surface) { - qDebug() << Q_FUNC_INFO << ": using mirsink, (this: " << this << ")"; - - m_videoSink = gst_element_factory_make("mirsink", "video-output"); - - connect(QGuiApplication::instance(), SIGNAL(focusWindowChanged(QWindow*)), - this, SLOT(handleFocusWindowChanged(QWindow*)), Qt::QueuedConnection); - - g_signal_connect(G_OBJECT(m_videoSink), "frame-ready", G_CALLBACK(handleFrameReady), - (gpointer)this); - } - - if (m_videoSink) { - gst_object_ref_sink(GST_OBJECT(m_videoSink)); - - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, - padBufferProbe, this, NULL); - } - - return m_videoSink; -} - -QWindow *QGstreamerMirTextureRenderer::createOffscreenWindow(const QSurfaceFormat &format) -{ - QWindow *w = new QWindow(); - w->setSurfaceType(QWindow::OpenGLSurface); - w->setFormat(format); - w->setGeometry(0, 0, 1, 1); - w->setFlags(w->flags() | Qt::WindowTransparentForInput); - w->create(); - - return w; -} - -void QGstreamerMirTextureRenderer::handleFrameReady(gpointer userData) -{ - QGstreamerMirTextureRenderer *renderer = reinterpret_cast<QGstreamerMirTextureRenderer*>(userData); -#if 1 - QMutexLocker locker(&rendererInstance->m_mutex); - QMetaObject::invokeMethod(rendererInstance, "renderFrame", Qt::QueuedConnection); -#else - // FIXME! - //QMutexLocker locker(&renderer->m_mutex); - QMetaObject::invokeMethod(renderer, "renderFrame", Qt::QueuedConnection); -#endif -} - -void QGstreamerMirTextureRenderer::renderFrame() -{ - //qDebug() << Q_FUNC_INFO; - - if (m_context) - m_context->makeCurrent(); - - GstState pendingState = GST_STATE_NULL; - GstState newState = GST_STATE_NULL; - // Don't block and return immediately: - GstStateChangeReturn ret = gst_element_get_state(m_videoSink, &newState, - &pendingState, 0); - if (ret == GST_STATE_CHANGE_FAILURE || newState == GST_STATE_NULL|| - pendingState == GST_STATE_NULL) { - qWarning() << "Invalid state change for renderer, aborting"; - stopRenderer(); - return; - } - - if (!m_surface->isActive()) { - qDebug() << "m_surface is not active"; - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - GstCaps *caps = gst_pad_get_current_caps(pad); - - if (caps) { - // Get the native video size from the video sink - QSize newNativeSize = QGstUtils::capsCorrectedResolution(caps); - if (m_nativeSize != newNativeSize) { - m_nativeSize = newNativeSize; - emit nativeSizeChanged(); - } - gst_caps_unref(caps); - } - - // Start the surface - QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle); - qDebug() << "m_nativeSize: " << m_nativeSize; - qDebug() << "format: " << format; - if (!m_surface->start(format)) { - qWarning() << Q_FUNC_INFO << ": failed to start the video surface " << format; - return; - } - } - - QGstreamerMirTextureBuffer *buffer = new QGstreamerMirTextureBuffer(m_textureId); - //qDebug() << "frameSize: " << m_surface->surfaceFormat().frameSize(); - QVideoFrame frame(buffer, m_surface->surfaceFormat().frameSize(), - m_surface->surfaceFormat().pixelFormat()); - - frame.setMetaData("TextureId", m_textureId); - - // Display the video frame on the surface: - m_surface->present(frame); -} - -GstPadProbeReturn QGstreamerMirTextureRenderer::padBufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer userData) -{ - Q_UNUSED(pad); - Q_UNUSED(info); - - QGstreamerMirTextureRenderer *control = reinterpret_cast<QGstreamerMirTextureRenderer*>(userData); - QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); - - return GST_PAD_PROBE_REMOVE; -} - -void QGstreamerMirTextureRenderer::stopRenderer() -{ - if (m_surface) - m_surface->stop(); -} - -QAbstractVideoSurface *QGstreamerMirTextureRenderer::surface() const -{ - return m_surface; -} - -void QGstreamerMirTextureRenderer::setSurface(QAbstractVideoSurface *surface) -{ - qDebug() << Q_FUNC_INFO; - - if (m_surface != surface) { - qDebug() << "Saving current QGLContext"; - m_context = const_cast<QGLContext*>(QGLContext::currentContext()); - - if (m_videoSink) - gst_object_unref(GST_OBJECT(m_videoSink)); - - m_videoSink = 0; - - if (m_surface) { - disconnect(m_surface.data(), SIGNAL(supportedFormatsChanged()), - this, SLOT(handleFormatChange())); - } - - bool wasReady = isReady(); - - m_surface = surface; - - if (m_surface) { - connect(m_surface.data(), SIGNAL(supportedFormatsChanged()), - this, SLOT(handleFormatChange())); - } - - if (wasReady != isReady()) - emit readyChanged(isReady()); - - emit sinkChanged(); - } -} - -void QGstreamerMirTextureRenderer::setPlayerSession(const QGstreamerPlayerSession *playerSession) -{ - m_playerSession = const_cast<QGstreamerPlayerSession*>(playerSession); -} - -void QGstreamerMirTextureRenderer::handleFormatChange() -{ - qDebug() << "Supported formats list has changed, reload video output"; - - if (m_videoSink) - gst_object_unref(GST_OBJECT(m_videoSink)); - - m_videoSink = 0; - emit sinkChanged(); -} - -void QGstreamerMirTextureRenderer::updateNativeVideoSize() -{ - //qDebug() << Q_FUNC_INFO; - const QSize oldSize = m_nativeSize; - - if (m_videoSink) { - // Find video native size to update video widget size hint - GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); - GstCaps *caps = gst_pad_get_current_caps(pad); - - if (caps) { - m_nativeSize = QGstUtils::capsCorrectedResolution(caps); - gst_caps_unref(caps); - } - } else { - m_nativeSize = QSize(); - } - qDebug() << Q_FUNC_INFO << oldSize << m_nativeSize << m_videoSink; - - if (m_nativeSize != oldSize) - emit nativeSizeChanged(); -} - -void QGstreamerMirTextureRenderer::handleFocusWindowChanged(QWindow *window) -{ - qDebug() << Q_FUNC_INFO; - - QOpenGLContext *currContext = QOpenGLContext::currentContext(); - - QQuickWindow *w = dynamic_cast<QQuickWindow*>(window); - // If we don't have a GL context in the current thread, create one and share it - // with the render thread GL context - if (!currContext && !m_glContext) { - // This emulates the new QOffscreenWindow class with Qt5.1 - m_offscreenSurface = createOffscreenWindow(w->openglContext()->surface()->format()); - m_offscreenSurface->setParent(window); - - QOpenGLContext *shareContext = 0; - if (m_surface) - shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>()); - m_glContext = new QOpenGLContext; - m_glContext->setFormat(m_offscreenSurface->requestedFormat()); - - if (shareContext) - m_glContext->setShareContext(shareContext); - - if (!m_glContext->create()) - { - qWarning() << "Failed to create new shared context."; - return; - } - } - - if (m_glContext) - m_glContext->makeCurrent(m_offscreenSurface); - - if (m_textureId == 0) { - glGenTextures(1, &m_textureId); - qDebug() << "texture_id (handleFocusWindowChanged): " << m_textureId << endl; - g_object_set(G_OBJECT(m_videoSink), "texture-id", m_textureId, (char*)NULL); - } -} diff --git a/src/gsttools/qgstreamermirtexturerenderer_p.h b/src/gsttools/qgstreamermirtexturerenderer_p.h deleted file mode 100644 index 62150f7e1..000000000 --- a/src/gsttools/qgstreamermirtexturerenderer_p.h +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Canonical 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERMIRTEXTURERENDERER_H -#define QGSTREAMERMIRTEXTURERENDERER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qmediaplayer.h> -#include <qvideorenderercontrol.h> -#include <private/qvideosurfacegstsink_p.h> -#include <qabstractvideosurface.h> - -#include "qgstreamervideorendererinterface_p.h" - -QT_BEGIN_NAMESPACE - -class QGstreamerMirTextureBuffer; -class QGstreamerPlayerSession; -class QGLContext; -class QOpenGLContext; -class QSurfaceFormat; - -class QGstreamerMirTextureRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface -{ - Q_OBJECT - Q_INTERFACES(QGstreamerVideoRendererInterface) -public: - QGstreamerMirTextureRenderer(QObject *parent = 0, const QGstreamerPlayerSession *playerSession = 0); - virtual ~QGstreamerMirTextureRenderer(); - - QAbstractVideoSurface *surface() const; - void setSurface(QAbstractVideoSurface *surface); - - void setPlayerSession(const QGstreamerPlayerSession *playerSession); - - GstElement *videoSink(); - - void stopRenderer(); - bool isReady() const { return m_surface != 0; } - -signals: - void sinkChanged(); - void readyChanged(bool); - void nativeSizeChanged(); - -private slots: - void handleFormatChange(); - void updateNativeVideoSize(); - void handleFocusWindowChanged(QWindow *window); - void renderFrame(); - -private: - QWindow *createOffscreenWindow(const QSurfaceFormat &format); - static void handleFrameReady(gpointer userData); - static GstPadProbeReturn padBufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer userData); - - GstElement *m_videoSink; - QPointer<QAbstractVideoSurface> m_surface; - QPointer<QAbstractVideoSurface> m_glSurface; - QGLContext *m_context; - QOpenGLContext *m_glContext; - unsigned int m_textureId; - QWindow *m_offscreenSurface; - QGstreamerPlayerSession *m_playerSession; - QGstreamerMirTextureBuffer *m_textureBuffer; - QSize m_nativeSize; - - QMutex m_mutex; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERMIRTEXTURERENDRER_H diff --git a/src/gsttools/qgstreamerplayercontrol.cpp b/src/gsttools/qgstreamerplayercontrol.cpp index 7c96b682f..bd4c90ac5 100644 --- a/src/gsttools/qgstreamerplayercontrol.cpp +++ b/src/gsttools/qgstreamerplayercontrol.cpp @@ -362,14 +362,10 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice * m_currentResource = content; m_stream = stream; - QNetworkRequest request; + QNetworkRequest request = content.request(); - if (m_stream) { + if (m_stream) userStreamValid = stream->isOpen() && m_stream->isReadable(); - request = content.canonicalRequest(); - } else if (!content.isNull()) { - request = content.canonicalRequest(); - } #if !QT_CONFIG(gstreamer_app) m_session->loadFromUri(request); diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index c0998d7ae..b9e1c084f 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -179,13 +179,26 @@ void QGstreamerPlayerSession::initPlaybin() 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 46432a0a1..164e62f86 100644 --- a/src/gsttools/qgstreamervideowidget.cpp +++ b/src/gsttools/qgstreamervideowidget.cpp @@ -169,6 +169,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 48781b0c7..f8a9d79c1 100644 --- a/src/gsttools/qgstutils.cpp +++ b/src/gsttools/qgstutils.cpp @@ -1034,6 +1034,7 @@ struct VideoFormat static const VideoFormat qt_videoFormatLookup[] = { { QVideoFrame::Format_YUV420P, GST_VIDEO_FORMAT_I420 }, + { QVideoFrame::Format_YUV422P, GST_VIDEO_FORMAT_Y42B }, { QVideoFrame::Format_YV12 , GST_VIDEO_FORMAT_YV12 }, { QVideoFrame::Format_UYVY , GST_VIDEO_FORMAT_UYVY }, { QVideoFrame::Format_YUYV , GST_VIDEO_FORMAT_YUY2 }, @@ -1044,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 }, @@ -1086,6 +1089,7 @@ struct YuvFormat static const YuvFormat qt_yuvColorLookup[] = { { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, + { QVideoFrame::Format_YUV422P, GST_MAKE_FOURCC('Y','4','2','B'), 8 }, { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, @@ -1303,11 +1307,9 @@ void QGstUtils::setMetaData(GstElement *element, const QMap<QByteArray, QVariant gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); - QMapIterator<QByteArray, QVariant> it(data); - while (it.hasNext()) { - it.next(); + for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) { const QString tagName = QString::fromLatin1(it.key()); - const QVariant tagValue = it.value(); + const QVariant &tagValue = it.value(); switch (tagValue.type()) { case QVariant::String: @@ -1566,6 +1568,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 119fc55a1..3b458a978 100644 --- a/src/gsttools/qgstvideorenderersink.cpp +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -54,6 +54,25 @@ #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 @@ -68,13 +87,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); } @@ -89,8 +128,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); @@ -136,6 +188,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() @@ -245,6 +301,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) { @@ -451,6 +619,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; @@ -616,4 +785,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 b1e333566..84162814c 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 = true; + QAbstractVideoBuffer::HandleType m_handleType = QAbstractVideoBuffer::NoHandle; }; 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 = nullptr; GstCaps *m_startCaps = nullptr; GstBuffer *m_renderBuffer = nullptr; +#if QT_CONFIG(gstreamer_gl) + GstGLContext *m_gstGLDisplayContext = nullptr; +#endif bool m_notified = false; bool m_stop = false; @@ -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 = nullptr; |