From b898e5038b29a236578dc7cd1588bdbc0d40eac1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 5 Jan 2021 17:35:28 +0100 Subject: Move the gsttools into Qt Multimedia Let's not have a separate helper lib for those. The longer term goal is to get rid of platform specific plugins anyway. Change-Id: I24c9e984c696a4ecfc4693413ad51264f6926afe Reviewed-by: Doris Verria Reviewed-by: Lars Knoll --- src/CMakeLists.txt | 4 - src/gsttools/CMakeLists.txt | 98 -- src/gsttools/gsttools.pro | 85 - src/gsttools/qgstappsrc.cpp | 236 --- src/gsttools/qgstappsrc_p.h | 121 -- src/gsttools/qgstcodecsinfo.cpp | 256 --- src/gsttools/qgstcodecsinfo_p.h | 96 -- src/gsttools/qgstreameraudioinputselector.cpp | 126 -- src/gsttools/qgstreameraudioinputselector_p.h | 86 - src/gsttools/qgstreameraudioprobecontrol.cpp | 97 -- src/gsttools/qgstreameraudioprobecontrol_p.h | 90 - src/gsttools/qgstreamerbufferprobe.cpp | 121 -- src/gsttools/qgstreamerbufferprobe_p.h | 92 - src/gsttools/qgstreamerbushelper.cpp | 204 --- src/gsttools/qgstreamerbushelper_p.h | 104 -- src/gsttools/qgstreamermessage.cpp | 93 - src/gsttools/qgstreamermessage_p.h | 84 - src/gsttools/qgstreamerplayercontrol.cpp | 527 ------ src/gsttools/qgstreamerplayercontrol_p.h | 136 -- src/gsttools/qgstreamerplayersession.cpp | 1774 -------------------- src/gsttools/qgstreamerplayersession_p.h | 277 --- src/gsttools/qgstreamervideoinputdevicecontrol.cpp | 108 -- src/gsttools/qgstreamervideoinputdevicecontrol_p.h | 95 -- src/gsttools/qgstreamervideooverlay.cpp | 605 ------- src/gsttools/qgstreamervideooverlay_p.h | 127 -- src/gsttools/qgstreamervideoprobecontrol.cpp | 117 -- src/gsttools/qgstreamervideoprobecontrol_p.h | 96 -- src/gsttools/qgstreamervideorenderer.cpp | 128 -- src/gsttools/qgstreamervideorenderer_p.h | 94 -- src/gsttools/qgstreamervideorendererinterface.cpp | 44 - src/gsttools/qgstreamervideorendererinterface_p.h | 85 - src/gsttools/qgstreamervideowidget.cpp | 280 --- src/gsttools/qgstreamervideowidget_p.h | 128 -- src/gsttools/qgstreamervideowindow.cpp | 179 -- src/gsttools/qgstreamervideowindow_p.h | 127 -- src/gsttools/qgsttools_global_p.h | 70 - src/gsttools/qgstutils.cpp | 1150 ------------- src/gsttools/qgstutils_p.h | 157 -- src/gsttools/qgstvideobuffer.cpp | 119 -- src/gsttools/qgstvideobuffer_p.h | 89 - src/gsttools/qgstvideorendererplugin.cpp | 51 - src/gsttools/qgstvideorendererplugin_p.h | 108 -- src/gsttools/qgstvideorenderersink.cpp | 803 --------- src/gsttools/qgstvideorenderersink_p.h | 199 --- src/multimedia/CMakeLists.txt | 59 + src/multimedia/gstreamer/gstreamer.pri | 64 + src/multimedia/gstreamer/qgstappsrc.cpp | 236 +++ src/multimedia/gstreamer/qgstappsrc_p.h | 121 ++ src/multimedia/gstreamer/qgstcodecsinfo.cpp | 256 +++ src/multimedia/gstreamer/qgstcodecsinfo_p.h | 96 ++ .../gstreamer/qgstreameraudioinputselector.cpp | 126 ++ .../gstreamer/qgstreameraudioinputselector_p.h | 86 + .../gstreamer/qgstreameraudioprobecontrol.cpp | 97 ++ .../gstreamer/qgstreameraudioprobecontrol_p.h | 90 + src/multimedia/gstreamer/qgstreamerbufferprobe.cpp | 121 ++ src/multimedia/gstreamer/qgstreamerbufferprobe_p.h | 92 + src/multimedia/gstreamer/qgstreamerbushelper.cpp | 204 +++ src/multimedia/gstreamer/qgstreamerbushelper_p.h | 104 ++ src/multimedia/gstreamer/qgstreamermessage.cpp | 93 + src/multimedia/gstreamer/qgstreamermessage_p.h | 84 + .../gstreamer/qgstreamerplayercontrol.cpp | 527 ++++++ .../gstreamer/qgstreamerplayercontrol_p.h | 136 ++ .../gstreamer/qgstreamerplayersession.cpp | 1774 ++++++++++++++++++++ .../gstreamer/qgstreamerplayersession_p.h | 277 +++ .../qgstreamervideoinputdevicecontrol.cpp | 108 ++ .../qgstreamervideoinputdevicecontrol_p.h | 95 ++ .../gstreamer/qgstreamervideooverlay.cpp | 605 +++++++ .../gstreamer/qgstreamervideooverlay_p.h | 127 ++ .../gstreamer/qgstreamervideoprobecontrol.cpp | 117 ++ .../gstreamer/qgstreamervideoprobecontrol_p.h | 96 ++ .../gstreamer/qgstreamervideorenderer.cpp | 128 ++ .../gstreamer/qgstreamervideorenderer_p.h | 94 ++ .../gstreamer/qgstreamervideorendererinterface.cpp | 44 + .../gstreamer/qgstreamervideorendererinterface_p.h | 86 + src/multimedia/gstreamer/qgstreamervideowindow.cpp | 179 ++ src/multimedia/gstreamer/qgstreamervideowindow_p.h | 127 ++ src/multimedia/gstreamer/qgstutils.cpp | 1150 +++++++++++++ src/multimedia/gstreamer/qgstutils_p.h | 157 ++ src/multimedia/gstreamer/qgstvideobuffer.cpp | 119 ++ src/multimedia/gstreamer/qgstvideobuffer_p.h | 89 + .../gstreamer/qgstvideorendererplugin.cpp | 59 + .../gstreamer/qgstvideorendererplugin_p.h | 112 ++ src/multimedia/gstreamer/qgstvideorenderersink.cpp | 803 +++++++++ src/multimedia/gstreamer/qgstvideorenderersink_p.h | 199 +++ src/multimedia/multimedia.pro | 1 + src/multimediawidgets/CMakeLists.txt | 5 + src/multimediawidgets/gstreamer/gstreamer.pri | 4 + .../gstreamer/qgstreamervideowidget.cpp | 280 +++ .../gstreamer/qgstreamervideowidget_p.h | 128 ++ src/multimediawidgets/multimediawidgets.pro | 1 + src/plugins/gstreamer/audiodecoder/CMakeLists.txt | 2 +- src/plugins/gstreamer/camerabin/CMakeLists.txt | 2 +- src/plugins/gstreamer/camerabin/camerabin.pro | 2 + src/plugins/gstreamer/common.pri | 2 +- src/plugins/gstreamer/mediacapture/CMakeLists.txt | 2 +- src/plugins/gstreamer/mediaplayer/CMakeLists.txt | 2 +- src/plugins/videonode/imx6/CMakeLists.txt | 1 - src/plugins/videonode/imx6/imx6.pro | 2 +- src/src.pro | 13 - 99 files changed, 9564 insertions(+), 9686 deletions(-) delete mode 100644 src/gsttools/CMakeLists.txt delete mode 100644 src/gsttools/gsttools.pro delete mode 100644 src/gsttools/qgstappsrc.cpp delete mode 100644 src/gsttools/qgstappsrc_p.h delete mode 100644 src/gsttools/qgstcodecsinfo.cpp delete mode 100644 src/gsttools/qgstcodecsinfo_p.h delete mode 100644 src/gsttools/qgstreameraudioinputselector.cpp delete mode 100644 src/gsttools/qgstreameraudioinputselector_p.h delete mode 100644 src/gsttools/qgstreameraudioprobecontrol.cpp delete mode 100644 src/gsttools/qgstreameraudioprobecontrol_p.h delete mode 100644 src/gsttools/qgstreamerbufferprobe.cpp delete mode 100644 src/gsttools/qgstreamerbufferprobe_p.h delete mode 100644 src/gsttools/qgstreamerbushelper.cpp delete mode 100644 src/gsttools/qgstreamerbushelper_p.h delete mode 100644 src/gsttools/qgstreamermessage.cpp delete mode 100644 src/gsttools/qgstreamermessage_p.h delete mode 100644 src/gsttools/qgstreamerplayercontrol.cpp delete mode 100644 src/gsttools/qgstreamerplayercontrol_p.h delete mode 100644 src/gsttools/qgstreamerplayersession.cpp delete mode 100644 src/gsttools/qgstreamerplayersession_p.h delete mode 100644 src/gsttools/qgstreamervideoinputdevicecontrol.cpp delete mode 100644 src/gsttools/qgstreamervideoinputdevicecontrol_p.h delete mode 100644 src/gsttools/qgstreamervideooverlay.cpp delete mode 100644 src/gsttools/qgstreamervideooverlay_p.h delete mode 100644 src/gsttools/qgstreamervideoprobecontrol.cpp delete mode 100644 src/gsttools/qgstreamervideoprobecontrol_p.h delete mode 100644 src/gsttools/qgstreamervideorenderer.cpp delete mode 100644 src/gsttools/qgstreamervideorenderer_p.h delete mode 100644 src/gsttools/qgstreamervideorendererinterface.cpp delete mode 100644 src/gsttools/qgstreamervideorendererinterface_p.h delete mode 100644 src/gsttools/qgstreamervideowidget.cpp delete mode 100644 src/gsttools/qgstreamervideowidget_p.h delete mode 100644 src/gsttools/qgstreamervideowindow.cpp delete mode 100644 src/gsttools/qgstreamervideowindow_p.h delete mode 100644 src/gsttools/qgsttools_global_p.h delete mode 100644 src/gsttools/qgstutils.cpp delete mode 100644 src/gsttools/qgstutils_p.h delete mode 100644 src/gsttools/qgstvideobuffer.cpp delete mode 100644 src/gsttools/qgstvideobuffer_p.h delete mode 100644 src/gsttools/qgstvideorendererplugin.cpp delete mode 100644 src/gsttools/qgstvideorendererplugin_p.h delete mode 100644 src/gsttools/qgstvideorenderersink.cpp delete mode 100644 src/gsttools/qgstvideorenderersink_p.h create mode 100644 src/multimedia/gstreamer/gstreamer.pri create mode 100644 src/multimedia/gstreamer/qgstappsrc.cpp create mode 100644 src/multimedia/gstreamer/qgstappsrc_p.h create mode 100644 src/multimedia/gstreamer/qgstcodecsinfo.cpp create mode 100644 src/multimedia/gstreamer/qgstcodecsinfo_p.h create mode 100644 src/multimedia/gstreamer/qgstreameraudioinputselector.cpp create mode 100644 src/multimedia/gstreamer/qgstreameraudioinputselector_p.h create mode 100644 src/multimedia/gstreamer/qgstreameraudioprobecontrol.cpp create mode 100644 src/multimedia/gstreamer/qgstreameraudioprobecontrol_p.h create mode 100644 src/multimedia/gstreamer/qgstreamerbufferprobe.cpp create mode 100644 src/multimedia/gstreamer/qgstreamerbufferprobe_p.h create mode 100644 src/multimedia/gstreamer/qgstreamerbushelper.cpp create mode 100644 src/multimedia/gstreamer/qgstreamerbushelper_p.h create mode 100644 src/multimedia/gstreamer/qgstreamermessage.cpp create mode 100644 src/multimedia/gstreamer/qgstreamermessage_p.h create mode 100644 src/multimedia/gstreamer/qgstreamerplayercontrol.cpp create mode 100644 src/multimedia/gstreamer/qgstreamerplayercontrol_p.h create mode 100644 src/multimedia/gstreamer/qgstreamerplayersession.cpp create mode 100644 src/multimedia/gstreamer/qgstreamerplayersession_p.h create mode 100644 src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol.cpp create mode 100644 src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol_p.h create mode 100644 src/multimedia/gstreamer/qgstreamervideooverlay.cpp create mode 100644 src/multimedia/gstreamer/qgstreamervideooverlay_p.h create mode 100644 src/multimedia/gstreamer/qgstreamervideoprobecontrol.cpp create mode 100644 src/multimedia/gstreamer/qgstreamervideoprobecontrol_p.h create mode 100644 src/multimedia/gstreamer/qgstreamervideorenderer.cpp create mode 100644 src/multimedia/gstreamer/qgstreamervideorenderer_p.h create mode 100644 src/multimedia/gstreamer/qgstreamervideorendererinterface.cpp create mode 100644 src/multimedia/gstreamer/qgstreamervideorendererinterface_p.h create mode 100644 src/multimedia/gstreamer/qgstreamervideowindow.cpp create mode 100644 src/multimedia/gstreamer/qgstreamervideowindow_p.h create mode 100644 src/multimedia/gstreamer/qgstutils.cpp create mode 100644 src/multimedia/gstreamer/qgstutils_p.h create mode 100644 src/multimedia/gstreamer/qgstvideobuffer.cpp create mode 100644 src/multimedia/gstreamer/qgstvideobuffer_p.h create mode 100644 src/multimedia/gstreamer/qgstvideorendererplugin.cpp create mode 100644 src/multimedia/gstreamer/qgstvideorendererplugin_p.h create mode 100644 src/multimedia/gstreamer/qgstvideorenderersink.cpp create mode 100644 src/multimedia/gstreamer/qgstvideorenderersink_p.h create mode 100644 src/multimediawidgets/gstreamer/gstreamer.pri create mode 100644 src/multimediawidgets/gstreamer/qgstreamervideowidget.cpp create mode 100644 src/multimediawidgets/gstreamer/qgstreamervideowidget_p.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ac2d98d1..f6761cf84 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,9 +12,5 @@ if(TARGET Qt::Widgets) add_subdirectory(multimediawidgets) endif() -if(QT_FEATURE_gstreamer) - add_subdirectory(gsttools) -endif() - add_subdirectory(plugins) #special case end diff --git a/src/gsttools/CMakeLists.txt b/src/gsttools/CMakeLists.txt deleted file mode 100644 index 0620a4318..000000000 --- a/src/gsttools/CMakeLists.txt +++ /dev/null @@ -1,98 +0,0 @@ -# Generated from gsttools.pro. - -##################################################################### -## MultimediaGstTools Module: -##################################################################### - -qt_add_module(MultimediaGstTools - INTERNAL_MODULE - SOURCES - qgstcodecsinfo.cpp qgstcodecsinfo_p.h - qgstreameraudioinputselector.cpp qgstreameraudioinputselector_p.h - qgstreameraudioprobecontrol.cpp qgstreameraudioprobecontrol_p.h - qgstreamerbufferprobe.cpp qgstreamerbufferprobe_p.h - qgstreamerbushelper.cpp qgstreamerbushelper_p.h - qgstreamermessage.cpp qgstreamermessage_p.h - qgstreamerplayercontrol.cpp qgstreamerplayercontrol_p.h - qgstreamerplayersession.cpp qgstreamerplayersession_p.h - qgstreamervideoinputdevicecontrol.cpp qgstreamervideoinputdevicecontrol_p.h - qgstreamervideooverlay.cpp qgstreamervideooverlay_p.h - qgstreamervideoprobecontrol.cpp qgstreamervideoprobecontrol_p.h - qgstreamervideorenderer.cpp qgstreamervideorenderer_p.h - qgstreamervideorendererinterface.cpp qgstreamervideorendererinterface_p.h - qgstreamervideowindow.cpp qgstreamervideowindow_p.h - qgsttools_global_p.h - qgstutils.cpp qgstutils_p.h - qgstvideobuffer.cpp qgstvideobuffer_p.h - qgstvideorendererplugin.cpp qgstvideorendererplugin_p.h - qgstvideorenderersink.cpp qgstvideorenderersink_p.h - DEFINES - GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26 - PUBLIC_LIBRARIES - Qt::CorePrivate - Qt::GuiPrivate - Qt::MultimediaPrivate - gstreamer -) - -#### Keys ignored in scope 1:.:.:gsttools.pro:: -# MODULE = "multimediagsttools" - -## Scopes: -##################################################################### - -qt_extend_target(MultimediaGstTools CONDITION NOT static - DEFINES - QT_MAKEDLL -) - -qt_extend_target(MultimediaGstTools CONDITION QT_FEATURE_alsa - PUBLIC_LIBRARIES - alsa -) - -qt_extend_target(MultimediaGstTools CONDITION TARGET Qt::Widgets - SOURCES - qgstreamervideowidget.cpp qgstreamervideowidget_p.h # special case - PUBLIC_LIBRARIES - Qt::MultimediaWidgets -) - -#### Keys ignored in scope 4:.:.:gsttools.pro:TARGET Qt::Widgets: -# PRIVATE_HEADERS = "qgstreamervideowidget_p.h" - -qt_extend_target(MultimediaGstTools CONDITION QT_FEATURE_gstreamer_gl - PUBLIC_LIBRARIES - gstreamer_gl -) - -qt_extend_target(MultimediaGstTools CONDITION QT_FEATURE_gstreamer_app - SOURCES - qgstappsrc.cpp qgstappsrc_p.h # special case - PUBLIC_LIBRARIES - gstreamer_app -) - -#### Keys ignored in scope 6:.:.:gsttools.pro:QT_FEATURE_gstreamer_app: -# PRIVATE_HEADERS = "qgstappsrc_p.h" - -qt_extend_target(MultimediaGstTools CONDITION ANDROID - LIBRARIES - # Remove: L$ENV{GSTREAMER_ROOT_ANDROID}/armv7/lib - # Remove: Wl,--no-whole-archive - # Remove: Wl,--whole-archive - WrapIconv::WrapIconv - ffi - glib-2.0 - gmodule-2.0 - gobject-2.0 - gstapp-1.0 - gstaudio-1.0 - gstbase-1.0 - gstpbutils-1.0 - gstreamer-1.0 - gsttag-1.0 - gstvideo-1.0 - intl - orc-0.4 -) diff --git a/src/gsttools/gsttools.pro b/src/gsttools/gsttools.pro deleted file mode 100644 index 5271c396f..000000000 --- a/src/gsttools/gsttools.pro +++ /dev/null @@ -1,85 +0,0 @@ -TARGET = QtMultimediaGstTools -MODULE = multimediagsttools -CONFIG += internal_module - -QT = core-private multimedia-private gui-private - -!static:DEFINES += QT_MAKEDLL -DEFINES += GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26 - -qtConfig(alsa): \ - QMAKE_USE += alsa - -QMAKE_USE += gstreamer - -PRIVATE_HEADERS += \ - qgstreamerbushelper_p.h \ - qgstreamermessage_p.h \ - qgstutils_p.h \ - qgstvideobuffer_p.h \ - qgstreamerbufferprobe_p.h \ - qgstreamervideorendererinterface_p.h \ - qgstreameraudioinputselector_p.h \ - qgstreamervideorenderer_p.h \ - qgstreamervideoinputdevicecontrol_p.h \ - qgstcodecsinfo_p.h \ - qgstreamervideoprobecontrol_p.h \ - qgstreameraudioprobecontrol_p.h \ - qgstreamervideowindow_p.h \ - qgstreamervideooverlay_p.h \ - qgsttools_global_p.h \ - qgstreamerplayersession_p.h \ - qgstreamerplayercontrol_p.h \ - qgstvideorendererplugin_p.h \ - qgstvideorenderersink_p.h - -SOURCES += \ - qgstreamerbushelper.cpp \ - qgstreamermessage.cpp \ - qgstutils.cpp \ - qgstvideobuffer.cpp \ - qgstreamerbufferprobe.cpp \ - qgstreamervideorendererinterface.cpp \ - qgstreameraudioinputselector.cpp \ - qgstreamervideorenderer.cpp \ - qgstreamervideoinputdevicecontrol.cpp \ - qgstcodecsinfo.cpp \ - qgstreamervideoprobecontrol.cpp \ - qgstreameraudioprobecontrol.cpp \ - qgstreamervideowindow.cpp \ - qgstreamervideooverlay.cpp \ - qgstreamerplayersession.cpp \ - qgstreamerplayercontrol.cpp \ - qgstvideorendererplugin.cpp \ - qgstvideorenderersink.cpp - -qtHaveModule(widgets) { - QT += multimediawidgets - - PRIVATE_HEADERS += \ - qgstreamervideowidget_p.h - - SOURCES += \ - qgstreamervideowidget.cpp -} - -qtConfig(gstreamer_gl): QMAKE_USE += gstreamer_gl - -qtConfig(gstreamer_app) { - QMAKE_USE += gstreamer_app - PRIVATE_HEADERS += qgstappsrc_p.h - 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 deleted file mode 100644 index e8ec63bb9..000000000 --- a/src/gsttools/qgstappsrc.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/**************************************************************************** -** -** 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 - -#include "qgstappsrc_p.h" - -QGstAppSrc::QGstAppSrc(QObject *parent) - : QObject(parent) -{ - m_callbacks.need_data = &QGstAppSrc::on_need_data; - m_callbacks.enough_data = &QGstAppSrc::on_enough_data; - m_callbacks.seek_data = &QGstAppSrc::on_seek_data; -} - -QGstAppSrc::~QGstAppSrc() -{ - if (m_appSrc) - gst_object_unref(G_OBJECT(m_appSrc)); -} - -bool QGstAppSrc::setup(GstElement* appsrc) -{ - if (m_appSrc) { - gst_object_unref(G_OBJECT(m_appSrc)); - m_appSrc = 0; - } - - if (!appsrc || !m_stream) - return false; - - m_appSrc = GST_APP_SRC(appsrc); - gst_object_ref(G_OBJECT(m_appSrc)); - gst_app_src_set_callbacks(m_appSrc, (GstAppSrcCallbacks*)&m_callbacks, this, (GDestroyNotify)&QGstAppSrc::destroy_notify); - - g_object_get(G_OBJECT(m_appSrc), "max-bytes", &m_maxBytes, nullptr); - - if (m_sequential) - m_streamType = GST_APP_STREAM_TYPE_STREAM; - else - m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS; - gst_app_src_set_stream_type(m_appSrc, m_streamType); - gst_app_src_set_size(m_appSrc, (m_sequential) ? -1 : m_stream->size()); - - return true; -} - -void QGstAppSrc::setStream(QIODevice *stream) -{ - if (m_stream) { - disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); - disconnect(m_stream, SIGNAL(destroyed()), this, SLOT(streamDestroyed())); - m_stream = 0; - } - - if (m_appSrc) { - gst_object_unref(G_OBJECT(m_appSrc)); - m_appSrc = 0; - } - - m_dataRequestSize = ~0; - m_dataRequested = false; - m_enoughData = false; - m_forceData = false; - m_sequential = false; - m_maxBytes = 0; - - if (stream) { - m_stream = stream; - connect(m_stream, SIGNAL(destroyed()), SLOT(streamDestroyed())); - connect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); - m_sequential = m_stream->isSequential(); - } -} - -QIODevice *QGstAppSrc::stream() const -{ - return m_stream; -} - -GstAppSrc *QGstAppSrc::element() -{ - return m_appSrc; -} - -void QGstAppSrc::onDataReady() -{ - if (!m_enoughData) { - m_dataRequested = true; - pushDataToAppSrc(); - } -} - -void QGstAppSrc::streamDestroyed() -{ - if (sender() == m_stream) { - m_stream = 0; - sendEOS(); - } -} - -void QGstAppSrc::pushDataToAppSrc() -{ - if (!isStreamValid() || !m_appSrc) - return; - - if (m_dataRequested && !m_enoughData) { - qint64 size; - if (m_dataRequestSize == ~0u) - size = qMin(m_stream->bytesAvailable(), queueSize()); - else - size = qMin(m_stream->bytesAvailable(), (qint64)m_dataRequestSize); - - if (size) { - GstBuffer* buffer = gst_buffer_new_and_alloc(size); - - GstMapInfo mapInfo; - gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE); - void* bufferData = mapInfo.data; - - buffer->offset = m_stream->pos(); - qint64 bytesRead = m_stream->read((char*)bufferData, size); - buffer->offset_end = buffer->offset + bytesRead - 1; - - gst_buffer_unmap(buffer, &mapInfo); - - if (bytesRead > 0) { - m_dataRequested = false; - m_enoughData = false; - GstFlowReturn ret = gst_app_src_push_buffer (GST_APP_SRC (element()), buffer); - if (ret == GST_FLOW_ERROR) { - qWarning()<<"appsrc: push buffer error"; - } else if (ret == GST_FLOW_FLUSHING) { - qWarning()<<"appsrc: push buffer wrong state"; - } - } - } else if (!m_sequential) { - sendEOS(); - } - } else if (m_stream->atEnd() && !m_sequential) { - sendEOS(); - } -} - -bool QGstAppSrc::doSeek(qint64 value) -{ - if (isStreamValid()) - return stream()->seek(value); - return false; -} - - -gboolean QGstAppSrc::on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata) -{ - Q_UNUSED(element); - QGstAppSrc *self = reinterpret_cast(userdata); - if (self && self->isStreamValid()) { - if (!self->stream()->isSequential()) - QMetaObject::invokeMethod(self, "doSeek", Qt::AutoConnection, Q_ARG(qint64, arg0)); - } - else - return false; - - return true; -} - -void QGstAppSrc::on_enough_data(GstAppSrc *element, gpointer userdata) -{ - Q_UNUSED(element); - QGstAppSrc *self = reinterpret_cast(userdata); - if (self) - self->enoughData() = true; -} - -void QGstAppSrc::on_need_data(GstAppSrc *element, guint arg0, gpointer userdata) -{ - Q_UNUSED(element); - QGstAppSrc *self = reinterpret_cast(userdata); - if (self) { - self->dataRequested() = true; - self->enoughData() = false; - self->dataRequestSize()= arg0; - QMetaObject::invokeMethod(self, "pushDataToAppSrc", Qt::AutoConnection); - } -} - -void QGstAppSrc::destroy_notify(gpointer data) -{ - Q_UNUSED(data); -} - -void QGstAppSrc::sendEOS() -{ - if (!m_appSrc) - return; - - gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc)); - if (isStreamValid() && !stream()->isSequential()) - stream()->reset(); -} diff --git a/src/gsttools/qgstappsrc_p.h b/src/gsttools/qgstappsrc_p.h deleted file mode 100644 index c1c8cdabe..000000000 --- a/src/gsttools/qgstappsrc_p.h +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTAPPSRC_H -#define QGSTAPPSRC_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 -#include -#include - -#include -#include - -#if GST_VERSION_MAJOR < 1 -#include -#endif - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstAppSrc : public QObject -{ - Q_OBJECT -public: - QGstAppSrc(QObject *parent = 0); - ~QGstAppSrc(); - - bool setup(GstElement *); - - void setStream(QIODevice *); - QIODevice *stream() const; - - GstAppSrc *element(); - - qint64 queueSize() const { return m_maxBytes; } - - bool& enoughData() { return m_enoughData; } - bool& dataRequested() { return m_dataRequested; } - unsigned int& dataRequestSize() { return m_dataRequestSize; } - - bool isStreamValid() const - { - return m_stream != 0 && - m_stream->isOpen(); - } - -private slots: - void pushDataToAppSrc(); - bool doSeek(qint64); - void onDataReady(); - - void streamDestroyed(); -private: - static gboolean on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata); - static void on_enough_data(GstAppSrc *element, gpointer userdata); - static void on_need_data(GstAppSrc *element, uint arg0, gpointer userdata); - static void destroy_notify(gpointer data); - - void sendEOS(); - - QIODevice *m_stream = nullptr; - GstAppSrc *m_appSrc = nullptr; - bool m_sequential = false; - GstAppStreamType m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS; - GstAppSrcCallbacks m_callbacks; - qint64 m_maxBytes = 0; - unsigned int m_dataRequestSize = ~0; - bool m_dataRequested = false; - bool m_enoughData = false; - bool m_forceData = false; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstcodecsinfo.cpp b/src/gsttools/qgstcodecsinfo.cpp deleted file mode 100644 index bbf78124d..000000000 --- a/src/gsttools/qgstcodecsinfo.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstcodecsinfo_p.h" -#include "qgstutils_p.h" -#include - -#include - -static QSet streamTypes(GstElementFactory *factory, GstPadDirection direction) -{ - QSet types; - const GList *pads = gst_element_factory_get_static_pad_templates(factory); - for (const GList *pad = pads; pad; pad = g_list_next(pad)) { - GstStaticPadTemplate *templ = reinterpret_cast(pad->data); - if (templ->direction == direction) { - GstCaps *caps = gst_static_caps_get(&templ->static_caps); - for (uint i = 0; i < gst_caps_get_size(caps); ++i) { - GstStructure *structure = gst_caps_get_structure(caps, i); - types.insert(QString::fromUtf8(gst_structure_get_name(structure))); - } - gst_caps_unref(caps); - } - } - - return types; -} - -QGstCodecsInfo::QGstCodecsInfo(QGstCodecsInfo::ElementType elementType) -{ - updateCodecs(elementType); - for (auto &codec : supportedCodecs()) { - GstElementFactory *factory = gst_element_factory_find(codecElement(codec).constData()); - if (factory) { - GstPadDirection direction = elementType == Muxer ? GST_PAD_SINK : GST_PAD_SRC; - m_streamTypes.insert(codec, streamTypes(factory, direction)); - gst_object_unref(GST_OBJECT(factory)); - } - } -} - -QStringList QGstCodecsInfo::supportedCodecs() const -{ - return m_codecs; -} - -QString QGstCodecsInfo::codecDescription(const QString &codec) const -{ - return m_codecInfo.value(codec).description; -} - -QByteArray QGstCodecsInfo::codecElement(const QString &codec) const - -{ - return m_codecInfo.value(codec).elementName; -} - -QStringList QGstCodecsInfo::codecOptions(const QString &codec) const -{ - QStringList options; - - QByteArray elementName = m_codecInfo.value(codec).elementName; - if (elementName.isEmpty()) - return options; - - GstElement *element = gst_element_factory_make(elementName, nullptr); - if (element) { - guint numProperties; - GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(element), - &numProperties); - for (guint j = 0; j < numProperties; ++j) { - GParamSpec *property = properties[j]; - // ignore some properties - if (strcmp(property->name, "name") == 0 || strcmp(property->name, "parent") == 0) - continue; - - options.append(QLatin1String(property->name)); - } - g_free(properties); - gst_object_unref(element); - } - - return options; -} - -void QGstCodecsInfo::updateCodecs(ElementType elementType) -{ - m_codecs.clear(); - m_codecInfo.clear(); - - GList *elements = elementFactories(elementType); - - QSet fakeEncoderMimeTypes; - fakeEncoderMimeTypes << "unknown/unknown" - << "audio/x-raw-int" << "audio/x-raw-float" - << "video/x-raw-yuv" << "video/x-raw-rgb"; - - QSet fieldsToAdd; - fieldsToAdd << "mpegversion" << "layer" << "layout" << "raversion" - << "wmaversion" << "wmvversion" << "variant" << "systemstream"; - - GList *element = elements; - while (element) { - GstElementFactory *factory = (GstElementFactory *)element->data; - element = element->next; - - const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); - while (padTemplates) { - GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; - padTemplates = padTemplates->next; - - if (padTemplate->direction == GST_PAD_SRC) { - GstCaps *caps = gst_static_caps_get(&padTemplate->static_caps); - for (uint i=0; i::const_iterator it = m_codecInfo.constFind(codec); - if (it == m_codecInfo.constEnd() || it->rank < rank) { - if (it == m_codecInfo.constEnd()) - m_codecs.append(codec); - - CodecInfo info; - info.elementName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); - - gchar *description = gst_pb_utils_get_codec_description(newCaps); - info.description = QString::fromUtf8(description); - if (description) - g_free(description); - - info.rank = rank; - - m_codecInfo.insert(codec, info); - } - - gst_caps_unref(newCaps); - } - gst_caps_unref(caps); - } - } - } - - gst_plugin_feature_list_free(elements); -} - -GList *QGstCodecsInfo::elementFactories(ElementType elementType) const -{ - GstElementFactoryListType gstElementType = 0; - switch (elementType) { - case AudioEncoder: - gstElementType = GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER; - break; - case VideoEncoder: - gstElementType = GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER; - break; - case Muxer: - gstElementType = GST_ELEMENT_FACTORY_TYPE_MUXER; - break; - } - - GList *list = gst_element_factory_list_get_elements(gstElementType, GST_RANK_MARGINAL); - if (elementType == AudioEncoder) { - // Manually add "audioconvert" to the list - // to allow linking with various containers. - auto factory = gst_element_factory_find("audioconvert"); - if (factory) - list = g_list_prepend(list, factory); - } - - return list; -} - -QSet QGstCodecsInfo::supportedStreamTypes(const QString &codec) const -{ - return m_streamTypes.value(codec); -} - -QStringList QGstCodecsInfo::supportedCodecs(const QSet &types) const -{ - QStringList result; - for (auto &candidate : supportedCodecs()) { - auto candidateTypes = supportedStreamTypes(candidate); - if (candidateTypes.intersects(types)) - result << candidate; - } - - return result; -} diff --git a/src/gsttools/qgstcodecsinfo_p.h b/src/gsttools/qgstcodecsinfo_p.h deleted file mode 100644 index d7aadef21..000000000 --- a/src/gsttools/qgstcodecsinfo_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTCODECSINFO_H -#define QGSTCODECSINFO_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 -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstCodecsInfo -{ -public: - enum ElementType { AudioEncoder, VideoEncoder, Muxer }; - - struct CodecInfo { - QString description; - QByteArray elementName; - GstRank rank; - }; - - QGstCodecsInfo(ElementType elementType); - - QStringList supportedCodecs() const; - QString codecDescription(const QString &codec) const; - QByteArray codecElement(const QString &codec) const; - QStringList codecOptions(const QString &codec) const; - QSet supportedStreamTypes(const QString &codec) const; - QStringList supportedCodecs(const QSet &types) const; - -private: - void updateCodecs(ElementType elementType); - GList *elementFactories(ElementType elementType) const; - - QStringList m_codecs; - QMap m_codecInfo; - QMap> m_streamTypes; -}; - -Q_DECLARE_TYPEINFO(QGstCodecsInfo::CodecInfo, Q_MOVABLE_TYPE); - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstreameraudioinputselector.cpp b/src/gsttools/qgstreameraudioinputselector.cpp deleted file mode 100644 index dede1f661..000000000 --- a/src/gsttools/qgstreameraudioinputselector.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/**************************************************************************** -** -** 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 -#include "qgstreameraudioinputselector_p.h" - -#include -#include - -#include - -#include "qgstutils_p.h" - -QGstreamerAudioInputSelector::QGstreamerAudioInputSelector(QObject *parent) - :QAudioInputSelectorControl(parent) -{ - update(); -} - -QGstreamerAudioInputSelector::~QGstreamerAudioInputSelector() -{ -} - -QList QGstreamerAudioInputSelector::availableInputs() const -{ - return m_names; -} - -QString QGstreamerAudioInputSelector::inputDescription(const QString& name) const -{ - QString desc; - - for (int i = 0; i < m_names.size(); i++) { - if (m_names.at(i).compare(name) == 0) { - desc = m_descriptions.at(i); - break; - } - } - return desc; -} - -QString QGstreamerAudioInputSelector::defaultInput() const -{ - return m_defaultInput; -} - -QString QGstreamerAudioInputSelector::activeInput() const -{ - return m_audioInput; -} - -void QGstreamerAudioInputSelector::setActiveInput(const QString& name) -{ - if (m_audioInput.compare(name) != 0) { - m_audioInput = name; - emit activeInputChanged(name); - } -} - -void QGstreamerAudioInputSelector::update() -{ - QGstUtils::initializeGst(); - - m_names.clear(); - m_descriptions.clear(); - - const auto sources = QGstUtils::audioSources(); - for (auto *d : sources) { - auto *properties = gst_device_get_properties(d); - if (properties) { - auto *desc = gst_device_get_display_name(d); - QString description = QString::fromUtf8(desc); - g_free(desc); - if (description.contains(u"Monitor")) // ### is there a better way to skip those? - continue; - m_descriptions << description; - - auto *name = gst_structure_get_string(properties, "sysfs.path"); - m_names << QString::fromLatin1(name); - gboolean def; - if (gst_structure_get_boolean(properties, "is-default", &def) && def) - m_defaultInput = QString::fromLatin1(name); - - gst_structure_free(properties); - } - } - - if (m_names.size() > 0) - m_audioInput = m_names.at(0); -} diff --git a/src/gsttools/qgstreameraudioinputselector_p.h b/src/gsttools/qgstreameraudioinputselector_p.h deleted file mode 100644 index 8f839237e..000000000 --- a/src/gsttools/qgstreameraudioinputselector_p.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERAUDIOINPUTSELECTOR_H -#define QGSTREAMERAUDIOINPUTSELECTOR_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 -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstreamerAudioInputSelector : public QAudioInputSelectorControl -{ -Q_OBJECT -public: - QGstreamerAudioInputSelector(QObject *parent); - ~QGstreamerAudioInputSelector(); - - QList availableInputs() const override; - QString inputDescription(const QString &name) const override; - QString defaultInput() const override; - QString activeInput() const override; - -public Q_SLOTS: - void setActiveInput(const QString &name) override; - -private: - void update(); - - QString m_defaultInput; - QString m_audioInput; - QList m_names; - QList m_descriptions; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERAUDIOINPUTSELECTOR_H diff --git a/src/gsttools/qgstreameraudioprobecontrol.cpp b/src/gsttools/qgstreameraudioprobecontrol.cpp deleted file mode 100644 index 8b0415bde..000000000 --- a/src/gsttools/qgstreameraudioprobecontrol.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreameraudioprobecontrol_p.h" -#include - -QGstreamerAudioProbeControl::QGstreamerAudioProbeControl(QObject *parent) - : QMediaAudioProbeControl(parent) -{ -} - -QGstreamerAudioProbeControl::~QGstreamerAudioProbeControl() -{ -} - -void QGstreamerAudioProbeControl::probeCaps(GstCaps *caps) -{ - QAudioFormat format = QGstUtils::audioFormatForCaps(caps); - - QMutexLocker locker(&m_bufferMutex); - m_format = format; -} - -bool QGstreamerAudioProbeControl::probeBuffer(GstBuffer *buffer) -{ - qint64 position = GST_BUFFER_TIMESTAMP(buffer); - position = position >= 0 - ? position / G_GINT64_CONSTANT(1000) // microseconds - : -1; - - QByteArray data; - GstMapInfo info; - if (gst_buffer_map(buffer, &info, GST_MAP_READ)) { - data = QByteArray(reinterpret_cast(info.data), info.size); - gst_buffer_unmap(buffer, &info); - } else { - return true; - } - - QMutexLocker locker(&m_bufferMutex); - if (m_format.isValid()) { - if (!m_pendingBuffer.isValid()) - QMetaObject::invokeMethod(this, "bufferProbed", Qt::QueuedConnection); - m_pendingBuffer = QAudioBuffer(data, m_format, position); - } - - return true; -} - -void QGstreamerAudioProbeControl::bufferProbed() -{ - QAudioBuffer audioBuffer; - { - QMutexLocker locker(&m_bufferMutex); - if (!m_pendingBuffer.isValid()) - return; - audioBuffer = m_pendingBuffer; - m_pendingBuffer = QAudioBuffer(); - } - emit audioBufferProbed(audioBuffer); -} diff --git a/src/gsttools/qgstreameraudioprobecontrol_p.h b/src/gsttools/qgstreameraudioprobecontrol_p.h deleted file mode 100644 index 4fc5c7704..000000000 --- a/src/gsttools/qgstreameraudioprobecontrol_p.h +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERAUDIOPROBECONTROL_H -#define QGSTREAMERAUDIOPROBECONTROL_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 -#include -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstreamerAudioProbeControl - : public QMediaAudioProbeControl - , public QGstreamerBufferProbe - , public QSharedData -{ - Q_OBJECT -public: - explicit QGstreamerAudioProbeControl(QObject *parent); - virtual ~QGstreamerAudioProbeControl(); - -protected: - void probeCaps(GstCaps *caps) override; - bool probeBuffer(GstBuffer *buffer) override; - -private slots: - void bufferProbed(); - -private: - QAudioBuffer m_pendingBuffer; - QAudioFormat m_format; - QMutex m_bufferMutex; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERAUDIOPROBECONTROL_H diff --git a/src/gsttools/qgstreamerbufferprobe.cpp b/src/gsttools/qgstreamerbufferprobe.cpp deleted file mode 100644 index 230807466..000000000 --- a/src/gsttools/qgstreamerbufferprobe.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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 "qgstreamerbufferprobe_p.h" -#include "qgstutils_p.h" - -QT_BEGIN_NAMESPACE - -QGstreamerBufferProbe::QGstreamerBufferProbe(Flags flags) - : m_flags(flags) -{ -} - -QGstreamerBufferProbe::~QGstreamerBufferProbe() -{ -} - -void QGstreamerBufferProbe::addProbeToPad(GstPad *pad, bool downstream) -{ - if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) { - probeCaps(caps); - gst_caps_unref(caps); - } - if (m_flags & ProbeCaps) { - m_capsProbeId = gst_pad_add_probe( - pad, - downstream - ? GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM - : GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, - capsProbe, - this, - nullptr); - } - if (m_flags & ProbeBuffers) { - m_bufferProbeId = gst_pad_add_probe( - pad, GST_PAD_PROBE_TYPE_BUFFER, bufferProbe, this, nullptr); - } -} - -void QGstreamerBufferProbe::removeProbeFromPad(GstPad *pad) -{ - if (m_capsProbeId != -1) { - gst_pad_remove_probe(pad, m_capsProbeId); - m_capsProbeId = -1; - } - if (m_bufferProbeId != -1) { - gst_pad_remove_probe(pad, m_bufferProbeId); - m_bufferProbeId = -1; - } -} - -void QGstreamerBufferProbe::probeCaps(GstCaps *) -{ -} - -bool QGstreamerBufferProbe::probeBuffer(GstBuffer *) -{ - return true; -} - -GstPadProbeReturn QGstreamerBufferProbe::capsProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data) -{ - QGstreamerBufferProbe * const control = static_cast(user_data); - - if (GstEvent * const event = gst_pad_probe_info_get_event(info)) { - if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS) { - GstCaps *caps; - gst_event_parse_caps(event, &caps); - - control->probeCaps(caps); - } - } - return GST_PAD_PROBE_OK; -} - -GstPadProbeReturn QGstreamerBufferProbe::bufferProbe( - GstPad *, GstPadProbeInfo *info, gpointer user_data) -{ - QGstreamerBufferProbe * const control = static_cast(user_data); - if (GstBuffer * const buffer = gst_pad_probe_info_get_buffer(info)) - return control->probeBuffer(buffer) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP; - return GST_PAD_PROBE_OK; -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstreamerbufferprobe_p.h b/src/gsttools/qgstreamerbufferprobe_p.h deleted file mode 100644 index 46067325c..000000000 --- a/src/gsttools/qgstreamerbufferprobe_p.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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 QGSTREAMERBUFFERPROBE_H -#define QGSTREAMERBUFFERPROBE_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 -#include - -#include - - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstreamerBufferProbe -{ -public: - enum Flags - { - ProbeCaps = 0x01, - ProbeBuffers = 0x02, - ProbeAll = ProbeCaps | ProbeBuffers - }; - - explicit QGstreamerBufferProbe(Flags flags = ProbeAll); - virtual ~QGstreamerBufferProbe(); - - void addProbeToPad(GstPad *pad, bool downstream = true); - void removeProbeFromPad(GstPad *pad); - -protected: - virtual void probeCaps(GstCaps *caps); - virtual bool probeBuffer(GstBuffer *buffer); - -private: - static GstPadProbeReturn capsProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); - static GstPadProbeReturn bufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); - int m_capsProbeId = -1; - int m_bufferProbeId = -1; - const Flags m_flags; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERAUDIOPROBECONTROL_H diff --git a/src/gsttools/qgstreamerbushelper.cpp b/src/gsttools/qgstreamerbushelper.cpp deleted file mode 100644 index 2eb038dfa..000000000 --- a/src/gsttools/qgstreamerbushelper.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#include -#include -#include -#include - -#include "qgstreamerbushelper_p.h" - -QT_BEGIN_NAMESPACE - - -class QGstreamerBusHelperPrivate : public QObject -{ - Q_OBJECT -public: - QGstreamerBusHelperPrivate(QGstreamerBusHelper *parent, GstBus* bus) : - QObject(parent), - m_tag(0), - m_bus(bus), - m_helper(parent), - m_intervalTimer(nullptr) - { - // glib event loop can be disabled either by env variable or QT_NO_GLIB define, so check the dispacher - QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); - const bool hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); - if (!hasGlib) { - m_intervalTimer = new QTimer(this); - m_intervalTimer->setInterval(250); - connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); - m_intervalTimer->start(); - } else { - m_tag = gst_bus_add_watch_full(bus, G_PRIORITY_DEFAULT, busCallback, this, nullptr); - } - } - - ~QGstreamerBusHelperPrivate() - { - m_helper = 0; - delete m_intervalTimer; - - if (m_tag) - gst_bus_remove_watch(m_bus); - } - - GstBus* bus() const { return m_bus; } - -private slots: - void interval() - { - GstMessage* message; - while ((message = gst_bus_poll(m_bus, GST_MESSAGE_ANY, 0)) != 0) { - processMessage(message); - gst_message_unref(message); - } - } - -private: - void processMessage(GstMessage* message) - { - QGstreamerMessage msg(message); - doProcessMessage(msg); - } - - void queueMessage(GstMessage* message) - { - QGstreamerMessage msg(message); - QMetaObject::invokeMethod(this, "doProcessMessage", Qt::QueuedConnection, - Q_ARG(QGstreamerMessage, msg)); - } - - static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data) - { - Q_UNUSED(bus); - reinterpret_cast(data)->queueMessage(message); - return TRUE; - } - - guint m_tag; - GstBus* m_bus; - QGstreamerBusHelper* m_helper; - QTimer* m_intervalTimer; - -private slots: - void doProcessMessage(const QGstreamerMessage& msg) - { - for (QGstreamerBusMessageFilter *filter : qAsConst(busFilters)) { - if (filter->processBusMessage(msg)) - break; - } - emit m_helper->message(msg); - } - -public: - QMutex filterMutex; - QList syncFilters; - QList busFilters; -}; - - -static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstreamerBusHelperPrivate *d) -{ - Q_UNUSED(bus); - QMutexLocker lock(&d->filterMutex); - - for (QGstreamerSyncMessageFilter *filter : qAsConst(d->syncFilters)) { - if (filter->processSyncMessage(QGstreamerMessage(message))) { - gst_message_unref(message); - return GST_BUS_DROP; - } - } - - return GST_BUS_PASS; -} - - -/*! - \class QGstreamerBusHelper - \internal -*/ - -QGstreamerBusHelper::QGstreamerBusHelper(GstBus* bus, QObject* parent): - QObject(parent) -{ - d = new QGstreamerBusHelperPrivate(this, bus); - gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, d, 0); - gst_object_ref(GST_OBJECT(bus)); -} - -QGstreamerBusHelper::~QGstreamerBusHelper() -{ - gst_bus_set_sync_handler(d->bus(), 0, 0, 0); - gst_object_unref(GST_OBJECT(d->bus())); -} - -void QGstreamerBusHelper::installMessageFilter(QObject *filter) -{ - auto syncFilter = qobject_cast(filter); - if (syncFilter) { - QMutexLocker lock(&d->filterMutex); - if (!d->syncFilters.contains(syncFilter)) - d->syncFilters.append(syncFilter); - } - - auto busFilter = qobject_cast(filter); - if (busFilter && !d->busFilters.contains(busFilter)) - d->busFilters.append(busFilter); -} - -void QGstreamerBusHelper::removeMessageFilter(QObject *filter) -{ - auto syncFilter = qobject_cast(filter); - if (syncFilter) { - QMutexLocker lock(&d->filterMutex); - d->syncFilters.removeAll(syncFilter); - } - - auto busFilter = qobject_cast(filter); - if (busFilter) - d->busFilters.removeAll(busFilter); -} - -QT_END_NAMESPACE - -#include "qgstreamerbushelper.moc" diff --git a/src/gsttools/qgstreamerbushelper_p.h b/src/gsttools/qgstreamerbushelper_p.h deleted file mode 100644 index e784f6e41..000000000 --- a/src/gsttools/qgstreamerbushelper_p.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERBUSHELPER_P_H -#define QGSTREAMERBUSHELPER_P_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 -#include - -#include "qgstreamermessage_p.h" - -#include - -QT_BEGIN_NAMESPACE - -class QGstreamerSyncMessageFilter { -public: - //returns true if message was processed and should be dropped, false otherwise - virtual bool processSyncMessage(const QGstreamerMessage &message) = 0; -}; -#define QGstreamerSyncMessageFilter_iid "org.qt-project.qt.gstreamersyncmessagefilter/5.0" -Q_DECLARE_INTERFACE(QGstreamerSyncMessageFilter, QGstreamerSyncMessageFilter_iid) - - -class QGstreamerBusMessageFilter { -public: - //returns true if message was processed and should be dropped, false otherwise - virtual bool processBusMessage(const QGstreamerMessage &message) = 0; -}; -#define QGstreamerBusMessageFilter_iid "org.qt-project.qt.gstreamerbusmessagefilter/5.0" -Q_DECLARE_INTERFACE(QGstreamerBusMessageFilter, QGstreamerBusMessageFilter_iid) - - -class QGstreamerBusHelperPrivate; - -class Q_GSTTOOLS_EXPORT QGstreamerBusHelper : public QObject -{ - Q_OBJECT - friend class QGstreamerBusHelperPrivate; - -public: - QGstreamerBusHelper(GstBus* bus, QObject* parent = 0); - ~QGstreamerBusHelper(); - - void installMessageFilter(QObject *filter); - void removeMessageFilter(QObject *filter); - -signals: - void message(QGstreamerMessage const& message); - -private: - QGstreamerBusHelperPrivate *d = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstreamermessage.cpp b/src/gsttools/qgstreamermessage.cpp deleted file mode 100644 index 7191565e1..000000000 --- a/src/gsttools/qgstreamermessage.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** 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 - -#include "qgstreamermessage_p.h" - -QT_BEGIN_NAMESPACE - -static int wuchi = qRegisterMetaType(); - - -/*! - \class QGstreamerMessage - \internal -*/ - -QGstreamerMessage::QGstreamerMessage(GstMessage* message): - m_message(message) -{ - gst_message_ref(m_message); -} - -QGstreamerMessage::QGstreamerMessage(QGstreamerMessage const& m): - m_message(m.m_message) -{ - gst_message_ref(m_message); -} - - -QGstreamerMessage::~QGstreamerMessage() -{ - if (m_message != 0) - gst_message_unref(m_message); -} - -GstMessage* QGstreamerMessage::rawMessage() const -{ - return m_message; -} - -QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs) -{ - if (rhs.m_message != m_message) { - if (rhs.m_message != 0) - gst_message_ref(rhs.m_message); - - if (m_message != 0) - gst_message_unref(m_message); - - m_message = rhs.m_message; - } - - return *this; -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstreamermessage_p.h b/src/gsttools/qgstreamermessage_p.h deleted file mode 100644 index baeecc360..000000000 --- a/src/gsttools/qgstreamermessage_p.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERMESSAGE_P_H -#define QGSTREAMERMESSAGE_P_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 -#include - -#include - -QT_BEGIN_NAMESPACE - -// Required for QDoc workaround -class QString; - -class Q_GSTTOOLS_EXPORT QGstreamerMessage -{ -public: - QGstreamerMessage() = default; - QGstreamerMessage(GstMessage* message); - QGstreamerMessage(QGstreamerMessage const& m); - ~QGstreamerMessage(); - - GstMessage* rawMessage() const; - - QGstreamerMessage& operator=(QGstreamerMessage const& rhs); - -private: - GstMessage* m_message = nullptr; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QGstreamerMessage); - -#endif diff --git a/src/gsttools/qgstreamerplayercontrol.cpp b/src/gsttools/qgstreamerplayercontrol.cpp deleted file mode 100644 index 8ad2e0e93..000000000 --- a/src/gsttools/qgstreamerplayercontrol.cpp +++ /dev/null @@ -1,527 +0,0 @@ -/**************************************************************************** -** -** 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 -#include - -#include -#include -#include -#include - -#include -#include -#include - -//#define DEBUG_PLAYBIN - -QT_BEGIN_NAMESPACE - -QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent) - : QMediaPlayerControl(parent) - , m_session(session) -{ - connect(m_session, &QGstreamerPlayerSession::positionChanged, this, &QGstreamerPlayerControl::positionChanged); - connect(m_session, &QGstreamerPlayerSession::durationChanged, this, &QGstreamerPlayerControl::durationChanged); - connect(m_session, &QGstreamerPlayerSession::mutedStateChanged, this, &QGstreamerPlayerControl::mutedChanged); - connect(m_session, &QGstreamerPlayerSession::volumeChanged, this, &QGstreamerPlayerControl::volumeChanged); - connect(m_session, &QGstreamerPlayerSession::stateChanged, this, &QGstreamerPlayerControl::updateSessionState); - connect(m_session, &QGstreamerPlayerSession::bufferingProgressChanged, this, &QGstreamerPlayerControl::setBufferProgress); - connect(m_session, &QGstreamerPlayerSession::playbackFinished, this, &QGstreamerPlayerControl::processEOS); - connect(m_session, &QGstreamerPlayerSession::audioAvailableChanged, this, &QGstreamerPlayerControl::audioAvailableChanged); - connect(m_session, &QGstreamerPlayerSession::videoAvailableChanged, this, &QGstreamerPlayerControl::videoAvailableChanged); - connect(m_session, &QGstreamerPlayerSession::seekableChanged, this, &QGstreamerPlayerControl::seekableChanged); - connect(m_session, &QGstreamerPlayerSession::error, this, &QGstreamerPlayerControl::error); - connect(m_session, &QGstreamerPlayerSession::invalidMedia, this, &QGstreamerPlayerControl::handleInvalidMedia); - connect(m_session, &QGstreamerPlayerSession::playbackRateChanged, this, &QGstreamerPlayerControl::playbackRateChanged); -} - -QGstreamerPlayerControl::~QGstreamerPlayerControl() -{ -} - -qint64 QGstreamerPlayerControl::position() const -{ - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - return m_session->duration(); - - return m_pendingSeekPosition != -1 ? m_pendingSeekPosition : m_session->position(); -} - -qint64 QGstreamerPlayerControl::duration() const -{ - return m_session->duration(); -} - -QMediaPlayer::State QGstreamerPlayerControl::state() const -{ - return m_currentState; -} - -QMediaPlayer::MediaStatus QGstreamerPlayerControl::mediaStatus() const -{ - return m_mediaStatus; -} - -int QGstreamerPlayerControl::bufferStatus() const -{ - if (m_bufferProgress == -1) - return m_session->state() == QMediaPlayer::StoppedState ? 0 : 100; - - return m_bufferProgress; -} - -int QGstreamerPlayerControl::volume() const -{ - return m_session->volume(); -} - -bool QGstreamerPlayerControl::isMuted() const -{ - return m_session->isMuted(); -} - -bool QGstreamerPlayerControl::isSeekable() const -{ - return m_session->isSeekable(); -} - -QMediaTimeRange QGstreamerPlayerControl::availablePlaybackRanges() const -{ - return m_session->availablePlaybackRanges(); -} - -qreal QGstreamerPlayerControl::playbackRate() const -{ - return m_session->playbackRate(); -} - -void QGstreamerPlayerControl::setPlaybackRate(qreal rate) -{ - m_session->setPlaybackRate(rate); -} - -void QGstreamerPlayerControl::setPosition(qint64 pos) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << pos/1000.0; -#endif - - pushState(); - - if (m_mediaStatus == QMediaPlayer::EndOfMedia) { - m_mediaStatus = QMediaPlayer::LoadedMedia; - } - - if (m_currentState == QMediaPlayer::StoppedState) { - m_pendingSeekPosition = pos; - emit positionChanged(m_pendingSeekPosition); - } else if (m_session->isSeekable()) { - m_session->showPrerollFrames(true); - m_session->seek(pos); - m_pendingSeekPosition = -1; - } else if (m_session->state() == QMediaPlayer::StoppedState) { - m_pendingSeekPosition = pos; - emit positionChanged(m_pendingSeekPosition); - } else if (m_pendingSeekPosition != -1) { - m_pendingSeekPosition = -1; - emit positionChanged(m_pendingSeekPosition); - } - - popAndNotifyState(); -} - -void QGstreamerPlayerControl::play() -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - //m_userRequestedState is needed to know that we need to resume playback when resource-policy - //regranted the resources after lost, since m_currentState will become paused when resources are - //lost. - m_userRequestedState = QMediaPlayer::PlayingState; - playOrPause(QMediaPlayer::PlayingState); -} - -void QGstreamerPlayerControl::pause() -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - m_userRequestedState = QMediaPlayer::PausedState; - // If the playback has not been started yet but pause is requested. - // Seek to the beginning to show first frame. - if (m_pendingSeekPosition == -1 && m_session->position() == 0) - m_pendingSeekPosition = 0; - - playOrPause(QMediaPlayer::PausedState); -} - -void QGstreamerPlayerControl::playOrPause(QMediaPlayer::State newState) -{ - if (m_mediaStatus == QMediaPlayer::NoMedia) - return; - - pushState(); - - if (m_setMediaPending) { - m_mediaStatus = QMediaPlayer::LoadingMedia; - setMedia(m_currentResource, m_stream); - } - - if (m_mediaStatus == QMediaPlayer::EndOfMedia && m_pendingSeekPosition == -1) { - m_pendingSeekPosition = 0; - } - - // show prerolled frame if switching from stopped state - if (m_pendingSeekPosition == -1) { - m_session->showPrerollFrames(true); - } else if (m_session->state() == QMediaPlayer::StoppedState) { - // Don't evaluate the next two conditions. - } else if (m_session->isSeekable()) { - m_session->pause(); - m_session->showPrerollFrames(true); - m_session->seek(m_pendingSeekPosition); - m_pendingSeekPosition = -1; - } else { - m_pendingSeekPosition = -1; - } - - bool ok = false; - - //To prevent displaying the first video frame when playback is resumed - //the pipeline is paused instead of playing, seeked to requested position, - //and after seeking is finished (position updated) playback is restarted - //with show-preroll-frame enabled. - if (newState == QMediaPlayer::PlayingState && m_pendingSeekPosition == -1) - ok = m_session->play(); - else - ok = m_session->pause(); - - if (!ok) - newState = QMediaPlayer::StoppedState; - - if (m_mediaStatus == QMediaPlayer::InvalidMedia) - m_mediaStatus = QMediaPlayer::LoadingMedia; - - m_currentState = newState; - - if (m_mediaStatus == QMediaPlayer::EndOfMedia || m_mediaStatus == QMediaPlayer::LoadedMedia) { - if (m_bufferProgress == -1 || m_bufferProgress == 100) - m_mediaStatus = QMediaPlayer::BufferedMedia; - else - m_mediaStatus = QMediaPlayer::BufferingMedia; - } - - popAndNotifyState(); - - emit positionChanged(position()); -} - -void QGstreamerPlayerControl::stop() -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - m_userRequestedState = QMediaPlayer::StoppedState; - - pushState(); - - if (m_currentState != QMediaPlayer::StoppedState) { - m_currentState = QMediaPlayer::StoppedState; - m_session->showPrerollFrames(false); // stop showing prerolled frames in stop state - // Since gst is not going to send GST_STATE_PAUSED - // when pipeline is already paused, - // needs to update media status directly. - if (m_session->state() == QMediaPlayer::PausedState) - updateMediaStatus(); - else - m_session->pause(); - - if (m_mediaStatus != QMediaPlayer::EndOfMedia) { - m_pendingSeekPosition = 0; - emit positionChanged(position()); - } - } - - popAndNotifyState(); -} - -void QGstreamerPlayerControl::setVolume(int volume) -{ - m_session->setVolume(volume); -} - -void QGstreamerPlayerControl::setMuted(bool muted) -{ - m_session->setMuted(muted); -} - -QMediaContent QGstreamerPlayerControl::media() const -{ - return m_currentResource; -} - -const QIODevice *QGstreamerPlayerControl::mediaStream() const -{ - return m_stream; -} - -void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - - pushState(); - - m_currentState = QMediaPlayer::StoppedState; - QMediaContent oldMedia = m_currentResource; - m_pendingSeekPosition = -1; - m_session->showPrerollFrames(false); // do not show prerolled frames until pause() or play() explicitly called - m_setMediaPending = false; - - m_session->stop(); - - bool userStreamValid = false; - - if (m_bufferProgress != -1) { - m_bufferProgress = -1; - emit bufferStatusChanged(0); - } - - m_currentResource = content; - m_stream = stream; - - QNetworkRequest request = content.request(); - - if (m_stream) - userStreamValid = stream->isOpen() && m_stream->isReadable(); - -#if !QT_CONFIG(gstreamer_app) - m_session->loadFromUri(request); -#else - if (m_stream) { - if (userStreamValid){ - m_session->loadFromStream(request, m_stream); - } else { - m_mediaStatus = QMediaPlayer::InvalidMedia; - emit error(QMediaPlayer::FormatError, tr("Attempting to play invalid user stream")); - popAndNotifyState(); - return; - } - } else - m_session->loadFromUri(request); -#endif - -#if QT_CONFIG(gstreamer_app) - if (!request.url().isEmpty() || userStreamValid) { -#else - if (!request.url().isEmpty()) { -#endif - m_mediaStatus = QMediaPlayer::LoadingMedia; - m_session->pause(); - } else { - m_mediaStatus = QMediaPlayer::NoMedia; - setBufferProgress(0); - } - - if (m_currentResource != oldMedia) - emit mediaChanged(m_currentResource); - - emit positionChanged(position()); - - popAndNotifyState(); -} - -void QGstreamerPlayerControl::setVideoOutput(QObject *output) -{ - m_session->setVideoRenderer(output); -} - -bool QGstreamerPlayerControl::isAudioAvailable() const -{ - return m_session->isAudioAvailable(); -} - -bool QGstreamerPlayerControl::isVideoAvailable() const -{ - return m_session->isVideoAvailable(); -} - -void QGstreamerPlayerControl::updateSessionState(QMediaPlayer::State state) -{ - pushState(); - - if (state == QMediaPlayer::StoppedState) { - m_session->showPrerollFrames(false); - m_currentState = QMediaPlayer::StoppedState; - } - - if (state == QMediaPlayer::PausedState && m_currentState != QMediaPlayer::StoppedState) { - if (m_pendingSeekPosition != -1 && m_session->isSeekable()) { - m_session->showPrerollFrames(true); - m_session->seek(m_pendingSeekPosition); - } - m_pendingSeekPosition = -1; - - if (m_currentState == QMediaPlayer::PlayingState) { - if (m_bufferProgress == -1 || m_bufferProgress == 100) - m_session->play(); - } - } - - updateMediaStatus(); - - popAndNotifyState(); -} - -void QGstreamerPlayerControl::updateMediaStatus() -{ - //EndOfMedia status should be kept, until reset by pause, play or setMedia - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - return; - - pushState(); - QMediaPlayer::MediaStatus oldStatus = m_mediaStatus; - - switch (m_session->state()) { - case QMediaPlayer::StoppedState: - if (m_currentResource.isNull()) - m_mediaStatus = QMediaPlayer::NoMedia; - else if (oldStatus != QMediaPlayer::InvalidMedia) - m_mediaStatus = QMediaPlayer::LoadingMedia; - break; - - case QMediaPlayer::PlayingState: - case QMediaPlayer::PausedState: - if (m_currentState == QMediaPlayer::StoppedState) { - m_mediaStatus = QMediaPlayer::LoadedMedia; - } else { - if (m_bufferProgress == -1 || m_bufferProgress == 100) - m_mediaStatus = QMediaPlayer::BufferedMedia; - else - m_mediaStatus = QMediaPlayer::StalledMedia; - } - break; - } - - popAndNotifyState(); -} - -void QGstreamerPlayerControl::processEOS() -{ - pushState(); - m_mediaStatus = QMediaPlayer::EndOfMedia; - emit positionChanged(position()); - m_session->endOfMediaReset(); - - if (m_currentState != QMediaPlayer::StoppedState) { - m_currentState = QMediaPlayer::StoppedState; - m_session->showPrerollFrames(false); // stop showing prerolled frames in stop state - } - - popAndNotifyState(); -} - -void QGstreamerPlayerControl::setBufferProgress(int progress) -{ - if (m_bufferProgress == progress || m_mediaStatus == QMediaPlayer::NoMedia) - return; - -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << progress; -#endif - m_bufferProgress = progress; - - if (m_currentState == QMediaPlayer::PlayingState && - m_bufferProgress == 100 && - m_session->state() != QMediaPlayer::PlayingState) - m_session->play(); - - if (!m_session->isLiveSource() && m_bufferProgress < 100 && - (m_session->state() == QMediaPlayer::PlayingState || - m_session->pendingState() == QMediaPlayer::PlayingState)) - m_session->pause(); - - updateMediaStatus(); - - emit bufferStatusChanged(m_bufferProgress); -} - -void QGstreamerPlayerControl::handleInvalidMedia() -{ - pushState(); - m_mediaStatus = QMediaPlayer::InvalidMedia; - m_currentState = QMediaPlayer::StoppedState; - m_setMediaPending = true; - popAndNotifyState(); -} - -void QGstreamerPlayerControl::pushState() -{ - m_stateStack.push(m_currentState); - m_mediaStatusStack.push(m_mediaStatus); -} - -void QGstreamerPlayerControl::popAndNotifyState() -{ - Q_ASSERT(!m_stateStack.isEmpty()); - - QMediaPlayer::State oldState = m_stateStack.pop(); - QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatusStack.pop(); - - if (m_stateStack.isEmpty()) { - if (m_mediaStatus != oldMediaStatus) { -#ifdef DEBUG_PLAYBIN - qDebug() << "Media status changed:" << m_mediaStatus; -#endif - emit mediaStatusChanged(m_mediaStatus); - } - - if (m_currentState != oldState) { -#ifdef DEBUG_PLAYBIN - qDebug() << "State changed:" << m_currentState; -#endif - emit stateChanged(m_currentState); - } - } -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstreamerplayercontrol_p.h b/src/gsttools/qgstreamerplayercontrol_p.h deleted file mode 100644 index c4cc2b876..000000000 --- a/src/gsttools/qgstreamerplayercontrol_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERPLAYERCONTROL_P_H -#define QGSTREAMERPLAYERCONTROL_P_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 -#include -#include - -QT_BEGIN_NAMESPACE - -class QGstreamerPlayerSession; -class Q_GSTTOOLS_EXPORT QGstreamerPlayerControl : public QMediaPlayerControl -{ - Q_OBJECT - -public: - QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent = 0); - ~QGstreamerPlayerControl(); - - QGstreamerPlayerSession *session() { return m_session; } - - QMediaPlayer::State state() const override; - QMediaPlayer::MediaStatus mediaStatus() const override; - - qint64 position() const override; - qint64 duration() const override; - - int bufferStatus() const override; - - int volume() const override; - bool isMuted() const override; - - bool isAudioAvailable() const override; - bool isVideoAvailable() const override; - void setVideoOutput(QObject *output); - - bool isSeekable() const override; - QMediaTimeRange availablePlaybackRanges() const override; - - qreal playbackRate() const override; - void setPlaybackRate(qreal rate) override; - - QMediaContent media() const override; - const QIODevice *mediaStream() const override; - void setMedia(const QMediaContent&, QIODevice *) override; - -public Q_SLOTS: - void setPosition(qint64 pos) override; - - void play() override; - void pause() override; - void stop() override; - - void setVolume(int volume) override; - void setMuted(bool muted) override; - -private Q_SLOTS: - void updateSessionState(QMediaPlayer::State state); - void updateMediaStatus(); - void processEOS(); - void setBufferProgress(int progress); - - void handleInvalidMedia(); - -private: - void playOrPause(QMediaPlayer::State state); - - void pushState(); - void popAndNotifyState(); - - QGstreamerPlayerSession *m_session = nullptr; - QMediaPlayer::State m_userRequestedState = QMediaPlayer::StoppedState; - QMediaPlayer::State m_currentState = QMediaPlayer::StoppedState; - QMediaPlayer::MediaStatus m_mediaStatus = QMediaPlayer::NoMedia; - QStack m_stateStack; - QStack m_mediaStatusStack; - - int m_bufferProgress = -1; - qint64 m_pendingSeekPosition = -1; - bool m_setMediaPending = false; - QMediaContent m_currentResource; - QIODevice *m_stream = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp deleted file mode 100644 index 2729097b5..000000000 --- a/src/gsttools/qgstreamerplayersession.cpp +++ /dev/null @@ -1,1774 +0,0 @@ -/**************************************************************************** -** -** 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 -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define DEBUG_PLAYBIN - -QT_BEGIN_NAMESPACE - -static bool usePlaybinVolume() -{ - static enum { Yes, No, Unknown } status = Unknown; - if (status == Unknown) { - QByteArray v = qgetenv("QT_GSTREAMER_USE_PLAYBIN_VOLUME"); - bool value = !v.isEmpty() && v != "0" && v != "false"; - if (value) - status = Yes; - else - status = No; - } - return status == Yes; -} - -typedef enum { - GST_PLAY_FLAG_VIDEO = 0x00000001, - GST_PLAY_FLAG_AUDIO = 0x00000002, - GST_PLAY_FLAG_TEXT = 0x00000004, - GST_PLAY_FLAG_VIS = 0x00000008, - GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, - GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, - GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, - GST_PLAY_FLAG_DOWNLOAD = 0x00000080, - GST_PLAY_FLAG_BUFFERING = 0x000000100 -} GstPlayFlags; - -QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) - : QObject(parent) -{ - initPlaybin(); -} - -void QGstreamerPlayerSession::initPlaybin() -{ - m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, nullptr); - if (m_playbin) { - //GST_PLAY_FLAG_NATIVE_VIDEO omits configuration of ffmpegcolorspace and videoscale, - //since those elements are included in the video output bin when necessary. - int flags = GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO; - QByteArray envFlags = qgetenv("QT_GSTREAMER_PLAYBIN_FLAGS"); - if (!envFlags.isEmpty()) { - flags |= envFlags.toInt(); - } - g_object_set(G_OBJECT(m_playbin), "flags", flags, nullptr); - - const QByteArray envAudioSink = qgetenv("QT_GSTREAMER_PLAYBIN_AUDIOSINK"); - GstElement *audioSink = gst_element_factory_make(envAudioSink.isEmpty() ? "autoaudiosink" : envAudioSink, "audiosink"); - if (audioSink) { - if (usePlaybinVolume()) { - m_audioSink = audioSink; - m_volumeElement = m_playbin; - } else { - m_volumeElement = gst_element_factory_make("volume", "volumeelement"); - if (m_volumeElement) { - m_audioSink = gst_bin_new("audio-output-bin"); - - gst_bin_add_many(GST_BIN(m_audioSink), m_volumeElement, audioSink, nullptr); - gst_element_link(m_volumeElement, audioSink); - - GstPad *pad = gst_element_get_static_pad(m_volumeElement, "sink"); - gst_element_add_pad(GST_ELEMENT(m_audioSink), gst_ghost_pad_new("sink", pad)); - gst_object_unref(GST_OBJECT(pad)); - } else { - m_audioSink = audioSink; - m_volumeElement = m_playbin; - } - } - - g_object_set(G_OBJECT(m_playbin), "audio-sink", m_audioSink, nullptr); - addAudioBufferProbe(); - } - } - - static const auto convDesc = qEnvironmentVariable("QT_GSTREAMER_PLAYBIN_CONVERT"); - GError *err = nullptr; - auto convPipeline = !convDesc.isEmpty() ? convDesc.toLatin1().constData() : "identity"; - auto convElement = gst_parse_launch(convPipeline, &err); - if (err) { - qWarning() << "Error:" << convDesc << ":" << QLatin1String(err->message); - g_clear_error(&err); - } - m_videoIdentity = convElement; - - m_nullVideoSink = gst_element_factory_make("fakesink", nullptr); - g_object_set(G_OBJECT(m_nullVideoSink), "sync", true, nullptr); - gst_object_ref(GST_OBJECT(m_nullVideoSink)); - - 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", nullptr); - GstElement *colorConvert = gst_element_factory_make("glcolorconvert", nullptr); - gst_bin_add_many(GST_BIN(m_videoOutputBin), videoOutputSink, colorConvert, m_videoIdentity, m_nullVideoSink, nullptr); - gst_element_link_many(videoOutputSink, colorConvert, m_videoIdentity, nullptr); - } else { - gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_nullVideoSink, nullptr); - } -#else - gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_nullVideoSink, nullptr); -#endif - gst_element_link(m_videoIdentity, m_nullVideoSink); - - m_videoSink = m_nullVideoSink; - - // add ghostpads - 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)); - - if (m_playbin != 0) { - // Sort out messages - setBus(gst_element_get_bus(m_playbin)); - - g_object_set(G_OBJECT(m_playbin), "video-sink", m_videoOutputBin, nullptr); - - g_signal_connect(G_OBJECT(m_playbin), "notify::source", G_CALLBACK(playbinNotifySource), this); - g_signal_connect(G_OBJECT(m_playbin), "element-added", G_CALLBACK(handleElementAdded), this); - - if (usePlaybinVolume()) { - updateVolume(); - updateMuted(); - g_signal_connect(G_OBJECT(m_playbin), "notify::volume", G_CALLBACK(handleVolumeChange), this); - g_signal_connect(G_OBJECT(m_playbin), "notify::mute", G_CALLBACK(handleMutedChange), this); - } - - g_signal_connect(G_OBJECT(m_playbin), "video-changed", G_CALLBACK(handleStreamsChange), this); - g_signal_connect(G_OBJECT(m_playbin), "audio-changed", G_CALLBACK(handleStreamsChange), this); - g_signal_connect(G_OBJECT(m_playbin), "text-changed", G_CALLBACK(handleStreamsChange), this); - -#if QT_CONFIG(gstreamer_app) - g_signal_connect(G_OBJECT(m_playbin), "deep-notify::source", G_CALLBACK(configureAppSrcElement), this); -#endif - - m_pipeline = m_playbin; - gst_object_ref(GST_OBJECT(m_pipeline)); - } -} - -QGstreamerPlayerSession::~QGstreamerPlayerSession() -{ - if (m_pipeline) { - stop(); - - removeVideoBufferProbe(); - removeAudioBufferProbe(); - - delete m_busHelper; - m_busHelper = nullptr; - resetElements(); - } -} - -template -static inline void resetGstObject(T *&obj, T *v = nullptr) -{ - if (obj) - gst_object_unref(GST_OBJECT(obj)); - - obj = v; -} - -void QGstreamerPlayerSession::resetElements() -{ - setBus(nullptr); - resetGstObject(m_playbin); - resetGstObject(m_pipeline); - resetGstObject(m_nullVideoSink); - resetGstObject(m_videoOutputBin); - - m_audioSink = nullptr; - m_volumeElement = nullptr; - m_videoIdentity = nullptr; - m_pendingVideoSink = nullptr; - m_videoSink = nullptr; -} - -GstElement *QGstreamerPlayerSession::playbin() const -{ - return m_playbin; -} - -#if QT_CONFIG(gstreamer_app) -void QGstreamerPlayerSession::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerPlayerSession* self) -{ - Q_UNUSED(object); - Q_UNUSED(pspec); - - if (!self->appsrc()) - return; - - GstElement *appsrc; - g_object_get(orig, "source", &appsrc, nullptr); - - if (!self->appsrc()->setup(appsrc)) - qWarning()<<"Could not setup appsrc element"; - - g_object_unref(G_OBJECT(appsrc)); -} -#endif - -void QGstreamerPlayerSession::loadFromStream(const QNetworkRequest &request, QIODevice *appSrcStream) -{ -#if QT_CONFIG(gstreamer_app) -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - m_request = request; - m_duration = 0; - m_lastPosition = 0; - - if (!m_appSrc) - m_appSrc = new QGstAppSrc(this); - m_appSrc->setStream(appSrcStream); - - if (!parsePipeline() && m_playbin) { - m_tags.clear(); - emit tagsChanged(); - - g_object_set(G_OBJECT(m_playbin), "uri", "appsrc://", nullptr); - - if (!m_streamTypes.isEmpty()) { - m_streamProperties.clear(); - m_streamTypes.clear(); - - emit streamsChanged(); - } - } -#endif -} - -void QGstreamerPlayerSession::loadFromUri(const QNetworkRequest &request) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << request.url(); -#endif - m_request = request; - m_duration = 0; - m_lastPosition = 0; - -#if QT_CONFIG(gstreamer_app) - if (m_appSrc) { - m_appSrc->deleteLater(); - m_appSrc = 0; - } -#endif - - if (!parsePipeline() && m_playbin) { - m_tags.clear(); - emit tagsChanged(); - - g_object_set(G_OBJECT(m_playbin), "uri", m_request.url().toEncoded().constData(), nullptr); - - if (!m_streamTypes.isEmpty()) { - m_streamProperties.clear(); - m_streamTypes.clear(); - - emit streamsChanged(); - } - } -} - -bool QGstreamerPlayerSession::parsePipeline() -{ - if (m_request.url().scheme() != QLatin1String("gst-pipeline")) { - if (!m_playbin) { - resetElements(); - initPlaybin(); - updateVideoRenderer(); - } - return false; - } - - // Set current surface to video sink before creating a pipeline. - auto renderer = qobject_cast(m_videoOutput); - if (renderer) - QGstVideoRendererSink::setSurface(renderer->surface()); - - QString url = m_request.url().toString(QUrl::RemoveScheme); - QString desc = QUrl::fromPercentEncoding(url.toLatin1().constData()); - GError *err = nullptr; - GstElement *pipeline = gst_parse_launch(desc.toLatin1().constData(), &err); - if (err) { - auto errstr = QLatin1String(err->message); - qWarning() << "Error:" << desc << ":" << errstr; - emit error(QMediaPlayer::FormatError, errstr); - g_clear_error(&err); - } - - return setPipeline(pipeline); -} - -static void gst_foreach(GstIterator *it, const std::function &cmp) -{ - GValue value = G_VALUE_INIT; - while (gst_iterator_next (it, &value) == GST_ITERATOR_OK) { - auto child = static_cast(g_value_get_object(&value)); - if (cmp(child)) - break; - } - - gst_iterator_free(it); - g_value_unset(&value); -} - -bool QGstreamerPlayerSession::setPipeline(GstElement *pipeline) -{ - GstBus *bus = pipeline ? gst_element_get_bus(pipeline) : nullptr; - if (!bus) - return false; - - if (m_playbin) - gst_element_set_state(m_playbin, GST_STATE_NULL); - - resetElements(); - setBus(bus); - m_pipeline = pipeline; - - if (m_renderer) { - gst_foreach(gst_bin_iterate_sinks(GST_BIN(pipeline)), - [this](GstElement *child) { - if (qstrcmp(GST_OBJECT_NAME(child), "qtvideosink") == 0) { - m_renderer->setVideoSink(child); - return true; - } - return false; - }); - } - -#if QT_CONFIG(gstreamer_app) - if (m_appSrc) { - gst_foreach(gst_bin_iterate_sources(GST_BIN(pipeline)), - [this](GstElement *child) { - if (qstrcmp(qt_gst_element_get_factory_name(child), "appsrc") == 0) { - m_appSrc->setup(child); - return true; - } - return false; - }); - } -#endif - - emit pipelineChanged(); - return true; -} - -void QGstreamerPlayerSession::setBus(GstBus *bus) -{ - resetGstObject(m_bus, bus); - - // It might still accept gst messages. - if (m_busHelper) - m_busHelper->deleteLater(); - m_busHelper = nullptr; - - if (!m_bus) - return; - - m_busHelper = new QGstreamerBusHelper(m_bus, this); - m_busHelper->installMessageFilter(this); - - if (m_videoOutput) - m_busHelper->installMessageFilter(m_videoOutput); -} - -qint64 QGstreamerPlayerSession::duration() const -{ - return m_duration; -} - -qint64 QGstreamerPlayerSession::position() const -{ - gint64 position = 0; - - if (m_pipeline && qt_gst_element_query_position(m_pipeline, GST_FORMAT_TIME, &position)) - m_lastPosition = position / 1000000; - return m_lastPosition; -} - -qreal QGstreamerPlayerSession::playbackRate() const -{ - return m_playbackRate; -} - -void QGstreamerPlayerSession::setPlaybackRate(qreal rate) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << rate; -#endif - if (!qFuzzyCompare(m_playbackRate, rate)) { - m_playbackRate = rate; - if (m_pipeline && m_seekable) { - qint64 from = rate > 0 ? position() : 0; - qint64 to = rate > 0 ? duration() : position(); - gst_element_seek(m_pipeline, rate, GST_FORMAT_TIME, - GstSeekFlags(GST_SEEK_FLAG_FLUSH), - GST_SEEK_TYPE_SET, from * 1000000, - GST_SEEK_TYPE_SET, to * 1000000); - } - emit playbackRateChanged(m_playbackRate); - } -} - -QMediaTimeRange QGstreamerPlayerSession::availablePlaybackRanges() const -{ - QMediaTimeRange ranges; - - if (duration() <= 0) - return ranges; - - //GST_FORMAT_TIME would be more appropriate, but unfortunately it's not supported. - //with GST_FORMAT_PERCENT media is treated as encoded with constant bitrate. - GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); - - if (!gst_element_query(m_pipeline, query)) { - gst_query_unref(query); - return ranges; - } - - gint64 rangeStart = 0; - gint64 rangeStop = 0; - for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) { - if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop)) - ranges.addInterval(rangeStart * duration() / 100, - rangeStop * duration() / 100); - } - - gst_query_unref(query); - - if (ranges.isEmpty() && !isLiveSource() && isSeekable()) - ranges.addInterval(0, duration()); - -#ifdef DEBUG_PLAYBIN - qDebug() << ranges; -#endif - - return ranges; -} - -int QGstreamerPlayerSession::activeStream(QMediaStreamsControl::StreamType streamType) const -{ - int streamNumber = -1; - if (m_playbin) { - switch (streamType) { - case QMediaStreamsControl::AudioStream: - g_object_get(G_OBJECT(m_playbin), "current-audio", &streamNumber, nullptr); - break; - case QMediaStreamsControl::VideoStream: - g_object_get(G_OBJECT(m_playbin), "current-video", &streamNumber, nullptr); - break; - case QMediaStreamsControl::SubPictureStream: - g_object_get(G_OBJECT(m_playbin), "current-text", &streamNumber, nullptr); - break; - default: - break; - } - } - - if (streamNumber >= 0) - streamNumber += m_playbin2StreamOffset.value(streamType,0); - - return streamNumber; -} - -void QGstreamerPlayerSession::setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << streamType << streamNumber; -#endif - - if (streamNumber >= 0) - streamNumber -= m_playbin2StreamOffset.value(streamType,0); - - if (m_playbin) { - switch (streamType) { - case QMediaStreamsControl::AudioStream: - g_object_set(G_OBJECT(m_playbin), "current-audio", streamNumber, nullptr); - break; - case QMediaStreamsControl::VideoStream: - g_object_set(G_OBJECT(m_playbin), "current-video", streamNumber, nullptr); - break; - case QMediaStreamsControl::SubPictureStream: - g_object_set(G_OBJECT(m_playbin), "current-text", streamNumber, nullptr); - break; - default: - break; - } - } -} - -int QGstreamerPlayerSession::volume() const -{ - return m_volume; -} - -bool QGstreamerPlayerSession::isMuted() const -{ - return m_muted; -} - -bool QGstreamerPlayerSession::isAudioAvailable() const -{ - return m_audioAvailable; -} - -static GstPadProbeReturn block_pad_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) -{ - Q_UNUSED(pad); - Q_UNUSED(info); - Q_UNUSED(user_data); - return GST_PAD_PROBE_OK; -} - -void QGstreamerPlayerSession::updateVideoRenderer() -{ -#ifdef DEBUG_PLAYBIN - qDebug() << "Video sink has chaged, reload video output"; -#endif - - if (m_videoOutput) - setVideoRenderer(m_videoOutput); -} - -void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - if (m_videoOutput != videoOutput) { - if (m_videoOutput) { - disconnect(m_videoOutput, SIGNAL(sinkChanged()), - this, SLOT(updateVideoRenderer())); - disconnect(m_videoOutput, SIGNAL(readyChanged(bool)), - this, SLOT(updateVideoRenderer())); - - m_busHelper->removeMessageFilter(m_videoOutput); - } - - m_videoOutput = videoOutput; - - if (m_videoOutput) { - connect(m_videoOutput, SIGNAL(sinkChanged()), - this, SLOT(updateVideoRenderer())); - connect(m_videoOutput, SIGNAL(readyChanged(bool)), - this, SLOT(updateVideoRenderer())); - - m_busHelper->installMessageFilter(m_videoOutput); - } - } - - m_renderer = qobject_cast(videoOutput); - emit rendererChanged(); - - // No sense to continue if custom pipeline requested. - if (!m_playbin) - return; - - GstElement *videoSink = 0; - if (m_renderer && m_renderer->isReady()) - videoSink = m_renderer->videoSink(); - - if (!videoSink) - videoSink = m_nullVideoSink; - -#ifdef DEBUG_PLAYBIN - qDebug() << "Set video output:" << videoOutput; - qDebug() << "Current sink:" << (m_videoSink ? GST_ELEMENT_NAME(m_videoSink) : "") << m_videoSink - << "pending:" << (m_pendingVideoSink ? GST_ELEMENT_NAME(m_pendingVideoSink) : "") << m_pendingVideoSink - << "new sink:" << (videoSink ? GST_ELEMENT_NAME(videoSink) : "") << videoSink; -#endif - - if (m_pendingVideoSink == videoSink || - (m_pendingVideoSink == 0 && m_videoSink == videoSink)) { -#ifdef DEBUG_PLAYBIN - qDebug() << "Video sink has not changed, skip video output reconfiguration"; -#endif - return; - } - -#ifdef DEBUG_PLAYBIN - qDebug() << "Reconfigure video output"; -#endif - - if (m_state == QMediaPlayer::StoppedState) { -#ifdef DEBUG_PLAYBIN - qDebug() << "The pipeline has not started yet, pending state:" << m_pendingState; -#endif - //the pipeline has not started yet - flushVideoProbes(); - m_pendingVideoSink = 0; - gst_element_set_state(m_videoSink, GST_STATE_NULL); - gst_element_set_state(m_playbin, GST_STATE_NULL); - - removeVideoBufferProbe(); - - gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink); - - m_videoSink = videoSink; - - gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); - - bool linked = gst_element_link(m_videoIdentity, m_videoSink); - if (!linked) - qWarning() << "Linking video output element failed"; - - if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame") != 0) { - gboolean value = m_displayPrerolledFrame; - g_object_set(G_OBJECT(m_videoSink), "show-preroll-frame", value, nullptr); - } - - addVideoBufferProbe(); - - switch (m_pendingState) { - case QMediaPlayer::PausedState: - gst_element_set_state(m_playbin, GST_STATE_PAUSED); - break; - case QMediaPlayer::PlayingState: - gst_element_set_state(m_playbin, GST_STATE_PLAYING); - break; - default: - break; - } - - resumeVideoProbes(); - - } else { - if (m_pendingVideoSink) { -#ifdef DEBUG_PLAYBIN - qDebug() << "already waiting for pad to be blocked, just change the pending sink"; -#endif - m_pendingVideoSink = videoSink; - return; - } - - m_pendingVideoSink = videoSink; - -#ifdef DEBUG_PLAYBIN - qDebug() << "Blocking the video output pad..."; -#endif - - //block pads, async to avoid locking in paused state - GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); - this->pad_probe_id = gst_pad_add_probe(srcPad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BLOCKING), block_pad_cb, this, nullptr); - gst_object_unref(GST_OBJECT(srcPad)); - - //Unpause the sink to avoid waiting until the buffer is processed - //while the sink is paused. The pad will be blocked as soon as the current - //buffer is processed. - if (m_state == QMediaPlayer::PausedState) { -#ifdef DEBUG_PLAYBIN - qDebug() << "Starting video output to avoid blocking in paused state..."; -#endif - gst_element_set_state(m_videoSink, GST_STATE_PLAYING); - } - } -} - -void QGstreamerPlayerSession::finishVideoOutputChange() -{ - if (!m_playbin || !m_pendingVideoSink) - return; - -#ifdef DEBUG_PLAYBIN - qDebug() << "finishVideoOutputChange" << m_pendingVideoSink; -#endif - - GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); - - if (!gst_pad_is_blocked(srcPad)) { - //pad is not blocked, it's possible to swap outputs only in the null state - qWarning() << "Pad is not blocked yet, could not switch video sink"; - GstState identityElementState = GST_STATE_NULL; - gst_element_get_state(m_videoIdentity, &identityElementState, nullptr, GST_CLOCK_TIME_NONE); - if (identityElementState != GST_STATE_NULL) { - gst_object_unref(GST_OBJECT(srcPad)); - return; //can't change vo yet, received async call from the previous change - } - } - - if (m_pendingVideoSink == m_videoSink) { - qDebug() << "Abort, no change"; - //video output was change back to the current one, - //no need to torment the pipeline, just unblock the pad - if (gst_pad_is_blocked(srcPad)) - gst_pad_remove_probe(srcPad, this->pad_probe_id); - - m_pendingVideoSink = 0; - gst_object_unref(GST_OBJECT(srcPad)); - return; - } - - gst_element_set_state(m_videoSink, GST_STATE_NULL); - gst_element_unlink(m_videoIdentity, m_videoSink); - - removeVideoBufferProbe(); - - gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink); - - m_videoSink = m_pendingVideoSink; - m_pendingVideoSink = 0; - - gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); - - addVideoBufferProbe(); - - bool linked = gst_element_link(m_videoIdentity, m_videoSink); - - if (!linked) - qWarning() << "Linking video output element failed"; - -#ifdef DEBUG_PLAYBIN - qDebug() << "notify the video connector it has to emit a new segment message..."; -#endif - - GstState state = GST_STATE_VOID_PENDING; - - switch (m_pendingState) { - case QMediaPlayer::StoppedState: - state = GST_STATE_NULL; - break; - case QMediaPlayer::PausedState: - state = GST_STATE_PAUSED; - break; - case QMediaPlayer::PlayingState: - state = GST_STATE_PLAYING; - break; - } - - gst_element_set_state(m_videoSink, state); - - if (state == GST_STATE_NULL) - flushVideoProbes(); - - // Set state change that was deferred due the video output - // change being pending - gst_element_set_state(m_playbin, state); - - if (state != GST_STATE_NULL) - resumeVideoProbes(); - - //don't have to wait here, it will unblock eventually - if (gst_pad_is_blocked(srcPad)) - gst_pad_remove_probe(srcPad, this->pad_probe_id); - - gst_object_unref(GST_OBJECT(srcPad)); - -} - -bool QGstreamerPlayerSession::isVideoAvailable() const -{ - return m_videoAvailable; -} - -bool QGstreamerPlayerSession::isSeekable() const -{ - return m_seekable; -} - -bool QGstreamerPlayerSession::play() -{ - static bool dumpDot = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR"); - if (dumpDot) - gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_pipeline), GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL), "gst.play"); -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - - m_everPlayed = false; - if (m_pipeline) { - m_pendingState = QMediaPlayer::PlayingState; - if (gst_element_set_state(m_pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - qWarning() << "GStreamer; Unable to play -" << m_request.url().toString(); - m_pendingState = m_state = QMediaPlayer::StoppedState; - emit stateChanged(m_state); - } else { - resumeVideoProbes(); - return true; - } - } - - return false; -} - -bool QGstreamerPlayerSession::pause() -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - if (m_pipeline) { - m_pendingState = QMediaPlayer::PausedState; - if (m_pendingVideoSink != 0) - return true; - - if (gst_element_set_state(m_pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { - qWarning() << "GStreamer; Unable to pause -" << m_request.url().toString(); - m_pendingState = m_state = QMediaPlayer::StoppedState; - emit stateChanged(m_state); - } else { - resumeVideoProbes(); - return true; - } - } - - return false; -} - -void QGstreamerPlayerSession::stop() -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - m_everPlayed = false; - if (m_pipeline) { - - if (m_renderer) - m_renderer->stopRenderer(); - - flushVideoProbes(); - gst_element_set_state(m_pipeline, GST_STATE_NULL); - - m_lastPosition = 0; - QMediaPlayer::State oldState = m_state; - m_pendingState = m_state = QMediaPlayer::StoppedState; - - finishVideoOutputChange(); - - //we have to do it here, since gstreamer will not emit bus messages any more - setSeekable(false); - if (oldState != m_state) - emit stateChanged(m_state); - } -} - -bool QGstreamerPlayerSession::seek(qint64 ms) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << ms; -#endif - //seek locks when the video output sink is changing and pad is blocked - if (m_pipeline && !m_pendingVideoSink && m_state != QMediaPlayer::StoppedState && m_seekable) { - ms = qMax(ms,qint64(0)); - qint64 from = m_playbackRate > 0 ? ms : 0; - qint64 to = m_playbackRate > 0 ? duration() : ms; - - bool isSeeking = gst_element_seek(m_pipeline, m_playbackRate, GST_FORMAT_TIME, - GstSeekFlags(GST_SEEK_FLAG_FLUSH), - GST_SEEK_TYPE_SET, from * 1000000, - GST_SEEK_TYPE_SET, to * 1000000); - if (isSeeking) - m_lastPosition = ms; - - return isSeeking; - } - - return false; -} - -void QGstreamerPlayerSession::setVolume(int volume) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << volume; -#endif - - if (m_volume != volume) { - m_volume = volume; - - if (m_volumeElement) - g_object_set(G_OBJECT(m_volumeElement), "volume", m_volume / 100.0, nullptr); - - emit volumeChanged(m_volume); - } -} - -void QGstreamerPlayerSession::setMuted(bool muted) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << muted; -#endif - if (m_muted != muted) { - m_muted = muted; - - if (m_volumeElement) - g_object_set(G_OBJECT(m_volumeElement), "mute", m_muted ? TRUE : FALSE, nullptr); - - emit mutedStateChanged(m_muted); - } -} - - -void QGstreamerPlayerSession::setSeekable(bool seekable) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << seekable; -#endif - if (seekable != m_seekable) { - m_seekable = seekable; - emit seekableChanged(m_seekable); - } -} - -bool QGstreamerPlayerSession::processBusMessage(const QGstreamerMessage &message) -{ - GstMessage* gm = message.rawMessage(); - if (gm) { - //tag message comes from elements inside playbin, not from playbin itself - if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) { - GstTagList *tag_list; - gst_message_parse_tag(gm, &tag_list); - - QMap newTags = QGstUtils::gstTagListToMap(tag_list); - QMap::const_iterator it = newTags.constBegin(); - for ( ; it != newTags.constEnd(); ++it) - m_tags.insert(it.key(), it.value()); // overwrite existing tags - - gst_tag_list_free(tag_list); - - emit tagsChanged(); - } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_DURATION) { - updateDuration(); - } - -#ifdef DEBUG_PLAYBIN - if (m_sourceType == MMSSrc && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { - qDebug() << "Message from MMSSrc: " << GST_MESSAGE_TYPE(gm); - } else if (m_sourceType == RTSPSrc && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { - qDebug() << "Message from RTSPSrc: " << GST_MESSAGE_TYPE(gm); - } else { - qDebug() << "Message from " << GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)) << ":" << GST_MESSAGE_TYPE(gm); - } -#endif - - if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_BUFFERING) { - int progress = 0; - gst_message_parse_buffering(gm, &progress); - emit bufferingProgressChanged(progress); - } - - bool handlePlaybin2 = false; - if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) { - switch (GST_MESSAGE_TYPE(gm)) { - case GST_MESSAGE_STATE_CHANGED: - { - GstState oldState; - GstState newState; - GstState pending; - - gst_message_parse_state_changed(gm, &oldState, &newState, &pending); - -#ifdef DEBUG_PLAYBIN - static QStringList states = { - QStringLiteral("GST_STATE_VOID_PENDING"), QStringLiteral("GST_STATE_NULL"), - QStringLiteral("GST_STATE_READY"), QStringLiteral("GST_STATE_PAUSED"), - QStringLiteral("GST_STATE_PLAYING") }; - - qDebug() << QStringLiteral("state changed: old: %1 new: %2 pending: %3") \ - .arg(states[oldState]) \ - .arg(states[newState]) \ - .arg(states[pending]); -#endif - - switch (newState) { - case GST_STATE_VOID_PENDING: - case GST_STATE_NULL: - setSeekable(false); - finishVideoOutputChange(); - if (m_state != QMediaPlayer::StoppedState) - emit stateChanged(m_state = QMediaPlayer::StoppedState); - break; - case GST_STATE_READY: - setSeekable(false); - if (m_state != QMediaPlayer::StoppedState) - emit stateChanged(m_state = QMediaPlayer::StoppedState); - break; - case GST_STATE_PAUSED: - { - QMediaPlayer::State prevState = m_state; - m_state = QMediaPlayer::PausedState; - - //check for seekable - if (oldState == GST_STATE_READY) { - if (m_sourceType == SoupHTTPSrc || m_sourceType == MMSSrc) { - //since udpsrc is a live source, it is not applicable here - m_everPlayed = true; - } - - getStreamsInfo(); - updateVideoResolutionTag(); - - //gstreamer doesn't give a reliable indication the duration - //information is ready, GST_MESSAGE_DURATION is not sent by most elements - //the duration is queried up to 5 times with increasing delay - m_durationQueries = 5; - // This should also update the seekable flag. - updateDuration(); - - if (!qFuzzyCompare(m_playbackRate, qreal(1.0))) { - qreal rate = m_playbackRate; - m_playbackRate = 1.0; - setPlaybackRate(rate); - } - } - - if (m_state != prevState) - emit stateChanged(m_state); - - break; - } - case GST_STATE_PLAYING: - m_everPlayed = true; - if (m_state != QMediaPlayer::PlayingState) { - emit stateChanged(m_state = QMediaPlayer::PlayingState); - - // For rtsp streams duration information might not be available - // until playback starts. - if (m_duration <= 0) { - m_durationQueries = 5; - updateDuration(); - } - } - - break; - } - } - break; - - case GST_MESSAGE_EOS: - emit playbackFinished(); - break; - - case GST_MESSAGE_TAG: - case GST_MESSAGE_STREAM_STATUS: - case GST_MESSAGE_UNKNOWN: - break; - case GST_MESSAGE_ERROR: { - GError *err; - gchar *debug; - gst_message_parse_error(gm, &err, &debug); - if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND) - processInvalidMedia(QMediaPlayer::FormatError, tr("Cannot play stream of type: ")); - else - processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); - qWarning() << "Error:" << QString::fromUtf8(err->message); - g_error_free(err); - g_free(debug); - } - break; - case GST_MESSAGE_WARNING: - { - GError *err; - gchar *debug; - gst_message_parse_warning (gm, &err, &debug); - qWarning() << "Warning:" << QString::fromUtf8(err->message); - g_error_free (err); - g_free (debug); - } - break; - case GST_MESSAGE_INFO: -#ifdef DEBUG_PLAYBIN - { - GError *err; - gchar *debug; - gst_message_parse_info (gm, &err, &debug); - qDebug() << "Info:" << QString::fromUtf8(err->message); - g_error_free (err); - g_free (debug); - } -#endif - break; - case GST_MESSAGE_BUFFERING: - case GST_MESSAGE_STATE_DIRTY: - case GST_MESSAGE_STEP_DONE: - case GST_MESSAGE_CLOCK_PROVIDE: - case GST_MESSAGE_CLOCK_LOST: - case GST_MESSAGE_NEW_CLOCK: - case GST_MESSAGE_STRUCTURE_CHANGE: - case GST_MESSAGE_APPLICATION: - case GST_MESSAGE_ELEMENT: - break; - case GST_MESSAGE_SEGMENT_START: - { - const GstStructure *structure = gst_message_get_structure(gm); - qint64 position = g_value_get_int64(gst_structure_get_value(structure, "position")); - position /= 1000000; - m_lastPosition = position; - emit positionChanged(position); - } - break; - case GST_MESSAGE_SEGMENT_DONE: - break; - case GST_MESSAGE_LATENCY: - case GST_MESSAGE_ASYNC_START: - break; - case GST_MESSAGE_ASYNC_DONE: - { - gint64 position = 0; - if (qt_gst_element_query_position(m_pipeline, GST_FORMAT_TIME, &position)) { - position /= 1000000; - m_lastPosition = position; - emit positionChanged(position); - } - break; - } - case GST_MESSAGE_REQUEST_STATE: - case GST_MESSAGE_ANY: - break; - default: - break; - } - } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { - GError *err; - gchar *debug; - gst_message_parse_error(gm, &err, &debug); - // If the source has given up, so do we. - if (qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { - bool everPlayed = m_everPlayed; - // Try and differentiate network related resource errors from the others - if (!m_request.url().isRelative() && m_request.url().scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) != 0 ) { - if (everPlayed || - (err->domain == GST_RESOURCE_ERROR && ( - err->code == GST_RESOURCE_ERROR_BUSY || - err->code == GST_RESOURCE_ERROR_OPEN_READ || - err->code == GST_RESOURCE_ERROR_READ || - err->code == GST_RESOURCE_ERROR_SEEK || - err->code == GST_RESOURCE_ERROR_SYNC))) { - processInvalidMedia(QMediaPlayer::NetworkError, QString::fromUtf8(err->message)); - } else { - processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); - } - } - else - processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); - } else if (err->domain == GST_STREAM_ERROR - && (err->code == GST_STREAM_ERROR_DECRYPT || err->code == GST_STREAM_ERROR_DECRYPT_NOKEY)) { - processInvalidMedia(QMediaPlayer::AccessDeniedError, QString::fromUtf8(err->message)); - } else { - handlePlaybin2 = true; - } - if (!handlePlaybin2) - qWarning() << "Error:" << QString::fromUtf8(err->message); - g_error_free(err); - g_free(debug); - } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT - && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0 - && m_sourceType == UDPSrc - && gst_structure_has_name(gst_message_get_structure(gm), "GstUDPSrcTimeout")) { - //since udpsrc will not generate an error for the timeout event, - //we need to process its element message here and treat it as an error. - processInvalidMedia(m_everPlayed ? QMediaPlayer::NetworkError : QMediaPlayer::ResourceError, - tr("UDP source timeout")); - } else { - handlePlaybin2 = true; - } - - if (handlePlaybin2) { - if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_WARNING) { - GError *err; - gchar *debug; - gst_message_parse_warning(gm, &err, &debug); - if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND) - emit error(int(QMediaPlayer::FormatError), tr("Cannot play stream of type: ")); - // GStreamer shows warning for HTTP playlists - if (err && err->message) - qWarning() << "Warning:" << QString::fromUtf8(err->message); - g_error_free(err); - g_free(debug); - } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { - GError *err; - gchar *debug; - gst_message_parse_error(gm, &err, &debug); - - // Nearly all errors map to ResourceError - QMediaPlayer::Error qerror = QMediaPlayer::ResourceError; - if (err->domain == GST_STREAM_ERROR - && (err->code == GST_STREAM_ERROR_DECRYPT - || err->code == GST_STREAM_ERROR_DECRYPT_NOKEY)) { - qerror = QMediaPlayer::AccessDeniedError; - } - processInvalidMedia(qerror, QString::fromUtf8(err->message)); - if (err && err->message) - qWarning() << "Error:" << QString::fromUtf8(err->message); - - g_error_free(err); - g_free(debug); - } - } - } - - return false; -} - -void QGstreamerPlayerSession::getStreamsInfo() -{ - if (!m_playbin) - return; - - QList< QMap > oldProperties = m_streamProperties; - QList oldTypes = m_streamTypes; - QMap oldOffset = m_playbin2StreamOffset; - - //check if video is available: - bool haveAudio = false; - bool haveVideo = false; - m_streamProperties.clear(); - m_streamTypes.clear(); - m_playbin2StreamOffset.clear(); - - gint audioStreamsCount = 0; - gint videoStreamsCount = 0; - gint textStreamsCount = 0; - - g_object_get(G_OBJECT(m_playbin), "n-audio", &audioStreamsCount, nullptr); - g_object_get(G_OBJECT(m_playbin), "n-video", &videoStreamsCount, nullptr); - g_object_get(G_OBJECT(m_playbin), "n-text", &textStreamsCount, nullptr); - - haveAudio = audioStreamsCount > 0; - haveVideo = videoStreamsCount > 0; - - m_playbin2StreamOffset[QMediaStreamsControl::AudioStream] = 0; - m_playbin2StreamOffset[QMediaStreamsControl::VideoStream] = audioStreamsCount; - m_playbin2StreamOffset[QMediaStreamsControl::SubPictureStream] = audioStreamsCount+videoStreamsCount; - - for (int i=0; i streamProperties; - - int streamIndex = i - m_playbin2StreamOffset[streamType]; - - GstTagList *tags = 0; - switch (streamType) { - case QMediaStreamsControl::AudioStream: - g_signal_emit_by_name(G_OBJECT(m_playbin), "get-audio-tags", streamIndex, &tags); - break; - case QMediaStreamsControl::VideoStream: - g_signal_emit_by_name(G_OBJECT(m_playbin), "get-video-tags", streamIndex, &tags); - break; - case QMediaStreamsControl::SubPictureStream: - g_signal_emit_by_name(G_OBJECT(m_playbin), "get-text-tags", streamIndex, &tags); - break; - default: - break; - } - if (tags && GST_IS_TAG_LIST(tags)) { - gchar *languageCode = 0; - if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) - streamProperties[QMediaMetaData::Language] = QString::fromUtf8(languageCode); - - //qDebug() << "language for setream" << i << QString::fromUtf8(languageCode); - g_free (languageCode); - gst_tag_list_free(tags); - } - - m_streamProperties.append(streamProperties); - } - - bool emitAudioChanged = (haveAudio != m_audioAvailable); - bool emitVideoChanged = (haveVideo != m_videoAvailable); - - m_audioAvailable = haveAudio; - m_videoAvailable = haveVideo; - - if (emitAudioChanged) { - emit audioAvailableChanged(m_audioAvailable); - } - if (emitVideoChanged) { - emit videoAvailableChanged(m_videoAvailable); - } - - if (oldProperties != m_streamProperties || oldTypes != m_streamTypes || oldOffset != m_playbin2StreamOffset) - emit streamsChanged(); -} - -void QGstreamerPlayerSession::updateVideoResolutionTag() -{ - if (!m_videoIdentity) - return; - -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - QSize size; - QSize aspectRatio; - GstPad *pad = gst_element_get_static_pad(m_videoIdentity, "src"); - GstCaps *caps = qt_gst_pad_get_current_caps(pad); - - if (caps) { - const GstStructure *structure = gst_caps_get_structure(caps, 0); - gst_structure_get_int(structure, "width", &size.rwidth()); - gst_structure_get_int(structure, "height", &size.rheight()); - - gint aspectNum = 0; - gint aspectDenum = 0; - if (!size.isEmpty() && gst_structure_get_fraction( - structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { - if (aspectDenum > 0) - aspectRatio = QSize(aspectNum, aspectDenum); - } - gst_caps_unref(caps); - } - - gst_object_unref(GST_OBJECT(pad)); - - QSize currentSize = m_tags.value("resolution").toSize(); - QSize currentAspectRatio = m_tags.value("pixel-aspect-ratio").toSize(); - - if (currentSize != size || currentAspectRatio != aspectRatio) { - if (aspectRatio.isEmpty()) - m_tags.remove("pixel-aspect-ratio"); - - if (size.isEmpty()) { - m_tags.remove("resolution"); - } else { - m_tags.insert("resolution", QVariant(size)); - if (!aspectRatio.isEmpty()) - m_tags.insert("pixel-aspect-ratio", QVariant(aspectRatio)); - } - - emit tagsChanged(); - } -} - -void QGstreamerPlayerSession::updateDuration() -{ - gint64 gstDuration = 0; - int duration = 0; - - if (m_pipeline && qt_gst_element_query_duration(m_pipeline, GST_FORMAT_TIME, &gstDuration)) - duration = gstDuration / 1000000; - - if (m_duration != duration) { - m_duration = duration; - emit durationChanged(m_duration); - } - - gboolean seekable = false; - if (m_duration > 0) { - m_durationQueries = 0; - GstQuery *query = gst_query_new_seeking(GST_FORMAT_TIME); - if (gst_element_query(m_pipeline, query)) - gst_query_parse_seeking(query, 0, &seekable, 0, 0); - gst_query_unref(query); - } - setSeekable(seekable); - - if (m_durationQueries > 0) { - //increase delay between duration requests - int delay = 25 << (5 - m_durationQueries); - QTimer::singleShot(delay, this, SLOT(updateDuration())); - m_durationQueries--; - } -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << m_duration; -#endif -} - -void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpointer d) -{ - Q_UNUSED(p); - - GstElement *source = 0; - g_object_get(o, "source", &source, nullptr); - if (source == 0) - return; - -#ifdef DEBUG_PLAYBIN - qDebug() << "Playbin source added:" << G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)); -#endif - - // Set Headers - const QByteArray userAgentString("User-Agent"); - - QGstreamerPlayerSession *self = reinterpret_cast(d); - - // User-Agent - special case, souphhtpsrc will always set something, even if - // defined in extra-headers - if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "user-agent") != 0) { - g_object_set(G_OBJECT(source), "user-agent", - self->m_request.rawHeader(userAgentString).constData(), nullptr); - } - - // The rest - if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "extra-headers") != 0) { - GstStructure *extras = qt_gst_structure_new_empty("extras"); - - const auto rawHeaderList = self->m_request.rawHeaderList(); - for (const QByteArray &rawHeader : rawHeaderList) { - if (rawHeader == userAgentString) // Filter User-Agent - continue; - - GValue headerValue; - - memset(&headerValue, 0, sizeof(GValue)); - g_value_init(&headerValue, G_TYPE_STRING); - - g_value_set_string(&headerValue, - self->m_request.rawHeader(rawHeader).constData()); - - gst_structure_set_value(extras, rawHeader.constData(), &headerValue); - } - - if (gst_structure_n_fields(extras) > 0) - g_object_set(G_OBJECT(source), "extra-headers", extras, nullptr); - - gst_structure_free(extras); - } - - //set timeout property to 30 seconds - const int timeout = 30; - if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstUDPSrc") == 0) { - quint64 convertedTimeout = timeout; - // Gst 1.x -> nanosecond - convertedTimeout *= 1000000000; - g_object_set(G_OBJECT(source), "timeout", convertedTimeout, nullptr); - self->m_sourceType = UDPSrc; - //The udpsrc is always a live source. - self->m_isLiveSource = true; - - QUrlQuery query(self->m_request.url()); - const QString var = QLatin1String("udpsrc.caps"); - if (query.hasQueryItem(var)) { - GstCaps *caps = gst_caps_from_string(query.queryItemValue(var).toLatin1().constData()); - g_object_set(G_OBJECT(source), "caps", caps, nullptr); - gst_caps_unref(caps); - } - } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstSoupHTTPSrc") == 0) { - //souphttpsrc timeout unit = second - g_object_set(G_OBJECT(source), "timeout", guint(timeout), nullptr); - self->m_sourceType = SoupHTTPSrc; - //since gst_base_src_is_live is not reliable, so we check the source property directly - gboolean isLive = false; - g_object_get(G_OBJECT(source), "is-live", &isLive, nullptr); - self->m_isLiveSource = isLive; - } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstMMSSrc") == 0) { - self->m_sourceType = MMSSrc; - self->m_isLiveSource = gst_base_src_is_live(GST_BASE_SRC(source)); - g_object_set(G_OBJECT(source), "tcp-timeout", G_GUINT64_CONSTANT(timeout*1000000), nullptr); - } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstRTSPSrc") == 0) { - //rtspsrc acts like a live source and will therefore only generate data in the PLAYING state. - self->m_sourceType = RTSPSrc; - self->m_isLiveSource = true; - g_object_set(G_OBJECT(source), "buffer-mode", 1, nullptr); - } else { - self->m_sourceType = UnknownSrc; - self->m_isLiveSource = gst_base_src_is_live(GST_BASE_SRC(source)); - } - -#ifdef DEBUG_PLAYBIN - if (self->m_isLiveSource) - qDebug() << "Current source is a live source"; - else - qDebug() << "Current source is a non-live source"; -#endif - - if (self->m_videoSink) - g_object_set(G_OBJECT(self->m_videoSink), "sync", !self->m_isLiveSource, nullptr); - - gst_object_unref(source); -} - -bool QGstreamerPlayerSession::isLiveSource() const -{ - return m_isLiveSource; -} - -void QGstreamerPlayerSession::handleVolumeChange(GObject *o, GParamSpec *p, gpointer d) -{ - Q_UNUSED(o); - Q_UNUSED(p); - QGstreamerPlayerSession *session = reinterpret_cast(d); - QMetaObject::invokeMethod(session, "updateVolume", Qt::QueuedConnection); -} - -void QGstreamerPlayerSession::updateVolume() -{ - double volume = 1.0; - g_object_get(m_playbin, "volume", &volume, nullptr); - - if (m_volume != int(volume*100 + 0.5)) { - m_volume = int(volume*100 + 0.5); -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << m_volume; -#endif - emit volumeChanged(m_volume); - } -} - -void QGstreamerPlayerSession::handleMutedChange(GObject *o, GParamSpec *p, gpointer d) -{ - Q_UNUSED(o); - Q_UNUSED(p); - QGstreamerPlayerSession *session = reinterpret_cast(d); - QMetaObject::invokeMethod(session, "updateMuted", Qt::QueuedConnection); -} - -void QGstreamerPlayerSession::updateMuted() -{ - gboolean muted = FALSE; - g_object_get(G_OBJECT(m_playbin), "mute", &muted, nullptr); - if (m_muted != muted) { - m_muted = muted; -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << m_muted; -#endif - emit mutedStateChanged(muted); - } -} - -GstAutoplugSelectResult QGstreamerPlayerSession::handleAutoplugSelect(GstBin *bin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, QGstreamerPlayerSession *session) -{ - Q_UNUSED(bin); - Q_UNUSED(pad); - Q_UNUSED(caps); - - GstAutoplugSelectResult res = GST_AUTOPLUG_SELECT_TRY; - - // if VAAPI is available and can be used to decode but the current video sink cannot handle - // the decoded format, don't use it - const gchar *factoryName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); - if (g_str_has_prefix(factoryName, "vaapi")) { - GstPad *sinkPad = gst_element_get_static_pad(session->m_videoSink, "sink"); - GstCaps *sinkCaps = gst_pad_query_caps(sinkPad, nullptr); - - if (!gst_element_factory_can_src_any_caps(factory, sinkCaps)) - res = GST_AUTOPLUG_SELECT_SKIP; - - gst_object_unref(sinkPad); - gst_caps_unref(sinkCaps); - } - - return res; -} - -void QGstreamerPlayerSession::handleElementAdded(GstBin *bin, GstElement *element, QGstreamerPlayerSession *session) -{ - Q_UNUSED(bin); - //we have to configure queue2 element to enable media downloading - //and reporting available ranges, - //but it's added dynamically to playbin2 - - gchar *elementName = gst_element_get_name(element); - - if (g_str_has_prefix(elementName, "queue2")) { - // Disable on-disk buffering. - g_object_set(G_OBJECT(element), "temp-template", nullptr, nullptr); - } else if (g_str_has_prefix(elementName, "uridecodebin") || - g_str_has_prefix(elementName, "decodebin")) { - //listen for queue2 element added to uridecodebin/decodebin2 as well. - //Don't touch other bins since they may have unrelated queues - g_signal_connect(element, "element-added", - G_CALLBACK(handleElementAdded), session); - } - - g_free(elementName); -} - -void QGstreamerPlayerSession::handleStreamsChange(GstBin *bin, gpointer user_data) -{ - Q_UNUSED(bin); - - QGstreamerPlayerSession* session = reinterpret_cast(user_data); - QMetaObject::invokeMethod(session, "getStreamsInfo", Qt::QueuedConnection); -} - -//doing proper operations when detecting an invalidMedia: change media status before signal the erorr -void QGstreamerPlayerSession::processInvalidMedia(QMediaPlayer::Error errorCode, const QString& errorString) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO; -#endif - emit invalidMedia(); - stop(); - emit error(int(errorCode), errorString); -} - -void QGstreamerPlayerSession::showPrerollFrames(bool enabled) -{ -#ifdef DEBUG_PLAYBIN - qDebug() << Q_FUNC_INFO << enabled; -#endif - if (enabled != m_displayPrerolledFrame && m_videoSink && - g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame") != 0) { - - gboolean value = enabled; - g_object_set(G_OBJECT(m_videoSink), "show-preroll-frame", value, nullptr); - m_displayPrerolledFrame = enabled; - } -} - -void QGstreamerPlayerSession::addProbe(QGstreamerVideoProbeControl* probe) -{ - Q_ASSERT(!m_videoProbe); - m_videoProbe = probe; - addVideoBufferProbe(); -} - -void QGstreamerPlayerSession::removeProbe(QGstreamerVideoProbeControl* probe) -{ - Q_ASSERT(m_videoProbe == probe); - removeVideoBufferProbe(); - m_videoProbe = 0; -} - -void QGstreamerPlayerSession::addProbe(QGstreamerAudioProbeControl* probe) -{ - Q_ASSERT(!m_audioProbe); - m_audioProbe = probe; - addAudioBufferProbe(); -} - -void QGstreamerPlayerSession::removeProbe(QGstreamerAudioProbeControl* probe) -{ - Q_ASSERT(m_audioProbe == probe); - removeAudioBufferProbe(); - m_audioProbe = 0; -} - -// This function is similar to stop(), -// but does not set m_everPlayed, m_lastPosition, -// and setSeekable() values. -void QGstreamerPlayerSession::endOfMediaReset() -{ - if (m_renderer) - m_renderer->stopRenderer(); - - flushVideoProbes(); - gst_element_set_state(m_pipeline, GST_STATE_PAUSED); - - QMediaPlayer::State oldState = m_state; - m_pendingState = m_state = QMediaPlayer::StoppedState; - - finishVideoOutputChange(); - - if (oldState != m_state) - emit stateChanged(m_state); -} - -void QGstreamerPlayerSession::removeVideoBufferProbe() -{ - if (!m_videoProbe) - return; - - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - if (pad) { - m_videoProbe->removeProbeFromPad(pad); - gst_object_unref(GST_OBJECT(pad)); - } -} - -void QGstreamerPlayerSession::addVideoBufferProbe() -{ - if (!m_videoProbe) - return; - - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - if (pad) { - m_videoProbe->addProbeToPad(pad); - gst_object_unref(GST_OBJECT(pad)); - } -} - -void QGstreamerPlayerSession::removeAudioBufferProbe() -{ - if (!m_audioProbe) - return; - - GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); - if (pad) { - m_audioProbe->removeProbeFromPad(pad); - gst_object_unref(GST_OBJECT(pad)); - } -} - -void QGstreamerPlayerSession::addAudioBufferProbe() -{ - if (!m_audioProbe) - return; - - GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); - if (pad) { - m_audioProbe->addProbeToPad(pad); - gst_object_unref(GST_OBJECT(pad)); - } -} - -void QGstreamerPlayerSession::flushVideoProbes() -{ - if (m_videoProbe) - m_videoProbe->startFlushing(); -} - -void QGstreamerPlayerSession::resumeVideoProbes() -{ - if (m_videoProbe) - m_videoProbe->stopFlushing(); -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstreamerplayersession_p.h b/src/gsttools/qgstreamerplayersession_p.h deleted file mode 100644 index 6ce773d7a..000000000 --- a/src/gsttools/qgstreamerplayersession_p.h +++ /dev/null @@ -1,277 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERPLAYERSESSION_P_H -#define QGSTREAMERPLAYERSESSION_P_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 -#include -#include -#include -#include -#include -#include -#include -#include - -#if QT_CONFIG(gstreamer_app) -#include -#endif - -#include - -QT_BEGIN_NAMESPACE - -class QGstreamerBusHelper; -class QGstreamerMessage; - -class QGstreamerVideoRendererInterface; -class QGstreamerVideoProbeControl; -class QGstreamerAudioProbeControl; - -typedef enum { - GST_AUTOPLUG_SELECT_TRY, - GST_AUTOPLUG_SELECT_EXPOSE, - GST_AUTOPLUG_SELECT_SKIP -} GstAutoplugSelectResult; - -class Q_GSTTOOLS_EXPORT QGstreamerPlayerSession - : public QObject - , public QGstreamerBusMessageFilter -{ -Q_OBJECT -Q_INTERFACES(QGstreamerBusMessageFilter) - -public: - QGstreamerPlayerSession(QObject *parent); - virtual ~QGstreamerPlayerSession(); - - GstElement *playbin() const; - GstElement *pipeline() const { return m_pipeline; } - QGstreamerBusHelper *bus() const { return m_busHelper; } - - QNetworkRequest request() const; - - QMediaPlayer::State state() const { return m_state; } - QMediaPlayer::State pendingState() const { return m_pendingState; } - - qint64 duration() const; - qint64 position() const; - - int volume() const; - bool isMuted() const; - - bool isAudioAvailable() const; - - void setVideoRenderer(QObject *renderer); - QGstreamerVideoRendererInterface *renderer() const { return m_renderer; } - bool isVideoAvailable() const; - - bool isSeekable() const; - - qreal playbackRate() const; - void setPlaybackRate(qreal rate); - - QMediaTimeRange availablePlaybackRanges() const; - - QMap tags() const { return m_tags; } - QMap streamProperties(int streamNumber) const { return m_streamProperties[streamNumber]; } - int streamCount() const { return m_streamProperties.count(); } - QMediaStreamsControl::StreamType streamType(int streamNumber) { return m_streamTypes.value(streamNumber, QMediaStreamsControl::UnknownStream); } - - int activeStream(QMediaStreamsControl::StreamType streamType) const; - void setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber); - - bool processBusMessage(const QGstreamerMessage &message) override; - -#if QT_CONFIG(gstreamer_app) - QGstAppSrc *appsrc() const { return m_appSrc; } - static void configureAppSrcElement(GObject*, GObject*, GParamSpec*,QGstreamerPlayerSession* _this); -#endif - - bool isLiveSource() const; - - void addProbe(QGstreamerVideoProbeControl* probe); - void removeProbe(QGstreamerVideoProbeControl* probe); - - void addProbe(QGstreamerAudioProbeControl* probe); - void removeProbe(QGstreamerAudioProbeControl* probe); - - void endOfMediaReset(); - -public slots: - void loadFromUri(const QNetworkRequest &url); - void loadFromStream(const QNetworkRequest &url, QIODevice *stream); - bool play(); - bool pause(); - void stop(); - - bool seek(qint64 pos); - - void setVolume(int volume); - void setMuted(bool muted); - - void showPrerollFrames(bool enabled); - -signals: - void durationChanged(qint64 duration); - void positionChanged(qint64 position); - void stateChanged(QMediaPlayer::State state); - void volumeChanged(int volume); - void mutedStateChanged(bool muted); - void audioAvailableChanged(bool audioAvailable); - void videoAvailableChanged(bool videoAvailable); - void bufferingProgressChanged(int percentFilled); - void playbackFinished(); - void tagsChanged(); - void streamsChanged(); - void seekableChanged(bool); - void error(int error, const QString &errorString); - void invalidMedia(); - void playbackRateChanged(qreal); - void rendererChanged(); - void pipelineChanged(); - -private slots: - void getStreamsInfo(); - void setSeekable(bool); - void finishVideoOutputChange(); - void updateVideoRenderer(); - void updateVideoResolutionTag(); - void updateVolume(); - void updateMuted(); - void updateDuration(); - -private: - static void playbinNotifySource(GObject *o, GParamSpec *p, gpointer d); - static void handleVolumeChange(GObject *o, GParamSpec *p, gpointer d); - static void handleMutedChange(GObject *o, GParamSpec *p, gpointer d); - static void handleElementAdded(GstBin *bin, GstElement *element, QGstreamerPlayerSession *session); - static void handleStreamsChange(GstBin *bin, gpointer user_data); - static GstAutoplugSelectResult handleAutoplugSelect(GstBin *bin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, QGstreamerPlayerSession *session); - - void processInvalidMedia(QMediaPlayer::Error errorCode, const QString& errorString); - - void removeVideoBufferProbe(); - void addVideoBufferProbe(); - void removeAudioBufferProbe(); - void addAudioBufferProbe(); - void flushVideoProbes(); - void resumeVideoProbes(); - bool parsePipeline(); - bool setPipeline(GstElement *pipeline); - void resetElements(); - void initPlaybin(); - void setBus(GstBus *bus); - - QNetworkRequest m_request; - QMediaPlayer::State m_state = QMediaPlayer::StoppedState; - QMediaPlayer::State m_pendingState = QMediaPlayer::StoppedState; - QGstreamerBusHelper *m_busHelper = nullptr; - GstElement *m_playbin = nullptr; - GstElement *m_pipeline = nullptr; - - GstElement *m_videoSink = nullptr; - - GstElement *m_videoOutputBin = nullptr; - GstElement *m_videoIdentity = nullptr; - GstElement *m_pendingVideoSink = nullptr; - GstElement *m_nullVideoSink = nullptr; - - GstElement *m_audioSink = nullptr; - GstElement *m_volumeElement = nullptr; - - GstBus *m_bus = nullptr; - QObject *m_videoOutput = nullptr; - QGstreamerVideoRendererInterface *m_renderer = nullptr; - -#if QT_CONFIG(gstreamer_app) - QGstAppSrc *m_appSrc = nullptr; -#endif - - QMap m_tags; - QList< QMap > m_streamProperties; - QList m_streamTypes; - QMap m_playbin2StreamOffset; - - QGstreamerVideoProbeControl *m_videoProbe = nullptr; - QGstreamerAudioProbeControl *m_audioProbe = nullptr; - - int m_volume = 100; - qreal m_playbackRate = 1.0; - bool m_muted = false; - bool m_audioAvailable = false; - bool m_videoAvailable = false; - bool m_seekable = false; - - mutable qint64 m_lastPosition = 0; - qint64 m_duration = 0; - int m_durationQueries = 0; - - bool m_displayPrerolledFrame = true; - - enum SourceType - { - UnknownSrc, - SoupHTTPSrc, - UDPSrc, - MMSSrc, - RTSPSrc, - }; - SourceType m_sourceType = UnknownSrc; - bool m_everPlayed = false; - bool m_isLiveSource = false; - - gulong pad_probe_id = 0; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERPLAYERSESSION_H diff --git a/src/gsttools/qgstreamervideoinputdevicecontrol.cpp b/src/gsttools/qgstreamervideoinputdevicecontrol.cpp deleted file mode 100644 index 088b97101..000000000 --- a/src/gsttools/qgstreamervideoinputdevicecontrol.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreamervideoinputdevicecontrol_p.h" - -#include -#include - -#include - -QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl(QObject *parent) - : QVideoDeviceSelectorControl(parent) -{ -} - -QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl( - GstElementFactory *factory, QObject *parent) - : QVideoDeviceSelectorControl(parent) - , m_factory(factory) -{ - if (m_factory) - gst_object_ref(GST_OBJECT(m_factory)); -} - -QGstreamerVideoInputDeviceControl::~QGstreamerVideoInputDeviceControl() -{ - if (m_factory) - gst_object_unref(GST_OBJECT(m_factory)); -} - -int QGstreamerVideoInputDeviceControl::deviceCount() const -{ - return QGstUtils::enumerateCameras().count(); -} - -QString QGstreamerVideoInputDeviceControl::deviceName(int index) const -{ - return QGstUtils::enumerateCameras().value(index).name; -} - -QString QGstreamerVideoInputDeviceControl::deviceDescription(int index) const -{ - return QGstUtils::enumerateCameras().value(index).description; -} - -QCamera::Position QGstreamerVideoInputDeviceControl::cameraPosition(int index) const -{ - return QGstUtils::enumerateCameras().value(index).position; -} - -int QGstreamerVideoInputDeviceControl::cameraOrientation(int index) const -{ - return QGstUtils::enumerateCameras().value(index).orientation; -} - -int QGstreamerVideoInputDeviceControl::defaultDevice() const -{ - return 0; -} - -int QGstreamerVideoInputDeviceControl::selectedDevice() const -{ - return m_selectedDevice; -} - -void QGstreamerVideoInputDeviceControl::setSelectedDevice(int index) -{ - // Always update selected device and proxy it to clients - m_selectedDevice = index; - emit selectedDeviceChanged(index); - emit selectedDeviceChanged(deviceName(index)); -} diff --git a/src/gsttools/qgstreamervideoinputdevicecontrol_p.h b/src/gsttools/qgstreamervideoinputdevicecontrol_p.h deleted file mode 100644 index c8080a724..000000000 --- a/src/gsttools/qgstreamervideoinputdevicecontrol_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOINPUTDEVICECONTROL_H -#define QGSTREAMERVIDEOINPUTDEVICECONTROL_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 -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstreamerVideoInputDeviceControl : public QVideoDeviceSelectorControl -{ -Q_OBJECT -public: - QGstreamerVideoInputDeviceControl(QObject *parent); - QGstreamerVideoInputDeviceControl(GstElementFactory *factory, QObject *parent); - ~QGstreamerVideoInputDeviceControl(); - - int deviceCount() const override; - - QString deviceName(int index) const override; - QString deviceDescription(int index) const override; - QCamera::Position cameraPosition(int index) const override; - int cameraOrientation(int index) const override; - - int defaultDevice() const override; - int selectedDevice() const override; - - static QString primaryCamera() { return tr("Main camera"); } - static QString secondaryCamera() { return tr("Front camera"); } - -public Q_SLOTS: - void setSelectedDevice(int index) override; - -private: - GstElementFactory *m_factory = nullptr; - - int m_selectedDevice = 0; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERAUDIOINPUTDEVICECONTROL_H diff --git a/src/gsttools/qgstreamervideooverlay.cpp b/src/gsttools/qgstreamervideooverlay.cpp deleted file mode 100644 index df3229736..000000000 --- a/src/gsttools/qgstreamervideooverlay.cpp +++ /dev/null @@ -1,605 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreamervideooverlay_p.h" - -#include -#include "qgstutils_p.h" - -#include - -#include - -QT_BEGIN_NAMESPACE - -struct ElementMap -{ - const char *qtPlatform; - const char *gstreamerElement; -}; - -// Ordered by descending priority -static constexpr ElementMap elementMap[] = -{ -#if QT_CONFIG(gstreamer_gl) - { "xcb", "glimagesink" }, -#endif - { "xcb", "vaapisink" }, - { "xcb", "xvimagesink" }, - { "xcb", "ximagesink" } -}; - -class QGstreamerSinkProperties -{ -public: - virtual ~QGstreamerSinkProperties() - { - } - - virtual bool hasShowPrerollFrame() const = 0; - virtual void reset() = 0; - virtual int brightness() const = 0; - virtual bool setBrightness(int brightness) = 0; - virtual int contrast() const = 0; - virtual bool setContrast(int contrast) = 0; - virtual int hue() const = 0; - virtual bool setHue(int hue) = 0; - virtual int saturation() const = 0; - virtual bool setSaturation(int saturation) = 0; - virtual Qt::AspectRatioMode aspectRatioMode() const = 0; - virtual void setAspectRatioMode(Qt::AspectRatioMode mode) = 0; -}; - -class QXVImageSinkProperties : public QGstreamerSinkProperties -{ -public: - QXVImageSinkProperties(GstElement *sink) - : m_videoSink(sink) - { - m_hasForceAspectRatio = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "force-aspect-ratio"); - m_hasBrightness = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness"); - m_hasContrast = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast"); - m_hasHue = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue"); - m_hasSaturation = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation"); - m_hasShowPrerollFrame = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame"); - } - - bool hasShowPrerollFrame() const override - { - return m_hasShowPrerollFrame; - } - - void reset() override - { - setAspectRatioMode(m_aspectRatioMode); - setBrightness(m_brightness); - setContrast(m_contrast); - setHue(m_hue); - setSaturation(m_saturation); - } - - int brightness() const override - { - int brightness = 0; - if (m_hasBrightness) - g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, nullptr); - - return brightness / 10; - } - - bool setBrightness(int brightness) override - { - m_brightness = brightness; - if (m_hasBrightness) - g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, nullptr); - - return m_hasBrightness; - } - - int contrast() const override - { - int contrast = 0; - if (m_hasContrast) - g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, nullptr); - - return contrast / 10; - } - - bool setContrast(int contrast) override - { - m_contrast = contrast; - if (m_hasContrast) - g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, nullptr); - - return m_hasContrast; - } - - int hue() const override - { - int hue = 0; - if (m_hasHue) - g_object_get(G_OBJECT(m_videoSink), "hue", &hue, nullptr); - - return hue / 10; - } - - bool setHue(int hue) override - { - m_hue = hue; - if (m_hasHue) - g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, nullptr); - - return m_hasHue; - } - - int saturation() const override - { - int saturation = 0; - if (m_hasSaturation) - g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, nullptr); - - return saturation / 10; - } - - bool setSaturation(int saturation) override - { - m_saturation = saturation; - if (m_hasSaturation) - g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, nullptr); - - return m_hasSaturation; - } - - Qt::AspectRatioMode aspectRatioMode() const override - { - Qt::AspectRatioMode mode = Qt::KeepAspectRatio; - if (m_hasForceAspectRatio) { - gboolean forceAR = false; - g_object_get(G_OBJECT(m_videoSink), "force-aspect-ratio", &forceAR, nullptr); - if (!forceAR) - mode = Qt::IgnoreAspectRatio; - } - - return mode; - } - - void setAspectRatioMode(Qt::AspectRatioMode mode) override - { - m_aspectRatioMode = mode; - if (m_hasForceAspectRatio) { - g_object_set(G_OBJECT(m_videoSink), - "force-aspect-ratio", - (mode == Qt::KeepAspectRatio), - nullptr); - } - } - -protected: - - GstElement *m_videoSink = nullptr; - bool m_hasForceAspectRatio = false; - bool m_hasBrightness = false; - bool m_hasContrast = false; - bool m_hasHue = false; - bool m_hasSaturation = false; - bool m_hasShowPrerollFrame = false; - Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio; - int m_brightness = 0; - int m_contrast = 0; - int m_hue = 0; - int m_saturation = 0; -}; - -class QVaapiSinkProperties : public QXVImageSinkProperties -{ -public: - QVaapiSinkProperties(GstElement *sink) - : QXVImageSinkProperties(sink) - { - // Set default values. - m_contrast = 1; - m_saturation = 1; - } - - int brightness() const override - { - gfloat brightness = 0; - if (m_hasBrightness) - g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, nullptr); - - return brightness * 100; // [-1,1] -> [-100,100] - } - - bool setBrightness(int brightness) override - { - m_brightness = brightness; - if (m_hasBrightness) { - gfloat v = brightness / 100.0; // [-100,100] -> [-1,1] - g_object_set(G_OBJECT(m_videoSink), "brightness", v, nullptr); - } - - return m_hasBrightness; - } - - int contrast() const override - { - gfloat contrast = 1; - if (m_hasContrast) - g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, nullptr); - - return (contrast - 1) * 100; // [0,2] -> [-100,100] - } - - bool setContrast(int contrast) override - { - m_contrast = contrast; - if (m_hasContrast) { - gfloat v = (contrast / 100.0) + 1; // [-100,100] -> [0,2] - g_object_set(G_OBJECT(m_videoSink), "contrast", v, nullptr); - } - - return m_hasContrast; - } - - int hue() const override - { - gfloat hue = 0; - if (m_hasHue) - g_object_get(G_OBJECT(m_videoSink), "hue", &hue, nullptr); - - return hue / 180 * 100; // [-180,180] -> [-100,100] - } - - bool setHue(int hue) override - { - m_hue = hue; - if (m_hasHue) { - gfloat v = hue / 100.0 * 180; // [-100,100] -> [-180,180] - g_object_set(G_OBJECT(m_videoSink), "hue", v, nullptr); - } - - return m_hasHue; - } - - int saturation() const override - { - gfloat saturation = 1; - if (m_hasSaturation) - g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, nullptr); - - return (saturation - 1) * 100; // [0,2] -> [-100,100] - } - - bool setSaturation(int saturation) override - { - m_saturation = saturation; - if (m_hasSaturation) { - gfloat v = (saturation / 100.0) + 1; // [-100,100] -> [0,2] - g_object_set(G_OBJECT(m_videoSink), "saturation", v, nullptr); - } - - return m_hasSaturation; - } -}; - -static bool qt_gst_element_is_functioning(GstElement *element) -{ - GstStateChangeReturn ret = gst_element_set_state(element, GST_STATE_READY); - if (ret == GST_STATE_CHANGE_SUCCESS) { - gst_element_set_state(element, GST_STATE_NULL); - return true; - } - - return false; -} - -static GstElement *findBestVideoSink() -{ - GstElement *choice = 0; - QString platform = QGuiApplication::platformName(); - - // We need a native window ID to use the GstVideoOverlay interface. - // Bail out if the Qt platform plugin in use cannot provide a sensible WId. - if (platform != QLatin1String("xcb")) - return 0; - - // First, try some known video sinks, depending on the Qt platform plugin in use. - for (auto i : elementMap) { -#if QT_CONFIG(gstreamer_gl) - if (!QGstUtils::useOpenGL() && qstrcmp(i.gstreamerElement, "glimagesink") == 0) - continue; -#endif - if (platform == QLatin1String(i.qtPlatform) - && (choice = gst_element_factory_make(i.gstreamerElement, nullptr))) { - - if (qt_gst_element_is_functioning(choice)) - return choice; - - gst_object_unref(choice); - choice = 0; - } - } - - // If none of the known video sinks are available, try to find one that implements the - // GstVideoOverlay interface and has autoplugging rank. - GList *list = qt_gst_video_sinks(); - for (GList *item = list; item != nullptr; item = item->next) { - GstElementFactory *f = GST_ELEMENT_FACTORY(item->data); - - if (!gst_element_factory_has_interface(f, QT_GSTREAMER_VIDEOOVERLAY_INTERFACE_NAME)) - continue; - - if (GstElement *el = gst_element_factory_create(f, nullptr)) { - if (qt_gst_element_is_functioning(el)) { - choice = el; - break; - } - - gst_object_unref(el); - } - } - - gst_plugin_feature_list_free(list); - - return choice; -} - -QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent, const QByteArray &elementName) - : QObject(parent) - , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps) -{ - GstElement *sink = nullptr; - if (!elementName.isEmpty()) - sink = gst_element_factory_make(elementName.constData(), nullptr); - else - sink = findBestVideoSink(); - - setVideoSink(sink); -} - -QGstreamerVideoOverlay::~QGstreamerVideoOverlay() -{ - if (m_videoSink) { - delete m_sinkProperties; - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - removeProbeFromPad(pad); - gst_object_unref(GST_OBJECT(pad)); - gst_object_unref(GST_OBJECT(m_videoSink)); - } -} - -GstElement *QGstreamerVideoOverlay::videoSink() const -{ - return m_videoSink; -} - -void QGstreamerVideoOverlay::setVideoSink(GstElement *sink) -{ - if (!sink) - return; - - if (m_videoSink) - gst_object_unref(GST_OBJECT(m_videoSink)); - - m_videoSink = sink; - qt_gst_object_ref_sink(GST_OBJECT(m_videoSink)); - - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); - addProbeToPad(pad); - gst_object_unref(GST_OBJECT(pad)); - - QString sinkName(QLatin1String(GST_OBJECT_NAME(sink))); - bool isVaapi = sinkName.startsWith(QLatin1String("vaapisink")); - delete m_sinkProperties; - m_sinkProperties = isVaapi ? new QVaapiSinkProperties(sink) : new QXVImageSinkProperties(sink); - - if (m_sinkProperties->hasShowPrerollFrame()) - g_signal_connect(m_videoSink, "notify::show-preroll-frame", - G_CALLBACK(showPrerollFrameChanged), this); -} - -QSize QGstreamerVideoOverlay::nativeVideoSize() const -{ - return m_nativeVideoSize; -} - -void QGstreamerVideoOverlay::setWindowHandle(WId id) -{ - m_windowId = id; - - if (isActive()) - setWindowHandle_helper(id); -} - -void QGstreamerVideoOverlay::setWindowHandle_helper(WId id) -{ - if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { - gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), id); - - // Properties need to be reset when changing the winId. - m_sinkProperties->reset(); - } -} - -void QGstreamerVideoOverlay::expose() -{ - if (!isActive()) - return; - - if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) - gst_video_overlay_expose(GST_VIDEO_OVERLAY(m_videoSink)); -} - -void QGstreamerVideoOverlay::setRenderRectangle(const QRect &rect) -{ - int x = -1; - int y = -1; - int w = -1; - int h = -1; - - if (!rect.isEmpty()) { - x = rect.x(); - y = rect.y(); - w = rect.width(); - h = rect.height(); - } - - if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) - gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink), x, y, w, h); -} - -bool QGstreamerVideoOverlay::processSyncMessage(const QGstreamerMessage &message) -{ - GstMessage* gm = message.rawMessage(); - - if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && - gst_structure_has_name(gst_message_get_structure(gm), "prepare-window-handle")) { - setWindowHandle_helper(m_windowId); - return true; - } - - return false; -} - -bool QGstreamerVideoOverlay::processBusMessage(const QGstreamerMessage &message) -{ - GstMessage* gm = message.rawMessage(); - - if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED && - GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink)) { - - updateIsActive(); - } - - return false; -} - -void QGstreamerVideoOverlay::probeCaps(GstCaps *caps) -{ - QSize size = QGstUtils::capsCorrectedResolution(caps); - if (size != m_nativeVideoSize) { - m_nativeVideoSize = size; - emit nativeVideoSizeChanged(); - } -} - -bool QGstreamerVideoOverlay::isActive() const -{ - return m_isActive; -} - -void QGstreamerVideoOverlay::updateIsActive() -{ - if (!m_videoSink) - return; - - GstState state = GST_STATE(m_videoSink); - gboolean showPreroll = true; - - if (m_sinkProperties->hasShowPrerollFrame()) - g_object_get(G_OBJECT(m_videoSink), "show-preroll-frame", &showPreroll, nullptr); - - bool newIsActive = (state == GST_STATE_PLAYING || (state == GST_STATE_PAUSED && showPreroll)); - - if (newIsActive != m_isActive) { - m_isActive = newIsActive; - emit activeChanged(); - } -} - -void QGstreamerVideoOverlay::showPrerollFrameChanged(GObject *, GParamSpec *, QGstreamerVideoOverlay *overlay) -{ - overlay->updateIsActive(); -} - -Qt::AspectRatioMode QGstreamerVideoOverlay::aspectRatioMode() const -{ - return m_sinkProperties->aspectRatioMode(); -} - -void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_sinkProperties->setAspectRatioMode(mode); -} - -int QGstreamerVideoOverlay::brightness() const -{ - return m_sinkProperties->brightness(); -} - -void QGstreamerVideoOverlay::setBrightness(int brightness) -{ - if (m_sinkProperties->setBrightness(brightness)) - emit brightnessChanged(brightness); -} - -int QGstreamerVideoOverlay::contrast() const -{ - return m_sinkProperties->contrast(); -} - -void QGstreamerVideoOverlay::setContrast(int contrast) -{ - if (m_sinkProperties->setContrast(contrast)) - emit contrastChanged(contrast); -} - -int QGstreamerVideoOverlay::hue() const -{ - return m_sinkProperties->hue(); -} - -void QGstreamerVideoOverlay::setHue(int hue) -{ - if (m_sinkProperties->setHue(hue)) - emit hueChanged(hue); -} - -int QGstreamerVideoOverlay::saturation() const -{ - return m_sinkProperties->saturation(); -} - -void QGstreamerVideoOverlay::setSaturation(int saturation) -{ - if (m_sinkProperties->setSaturation(saturation)) - emit saturationChanged(saturation); -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstreamervideooverlay_p.h b/src/gsttools/qgstreamervideooverlay_p.h deleted file mode 100644 index f2ca8a23b..000000000 --- a/src/gsttools/qgstreamervideooverlay_p.h +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOOVERLAY_P_H -#define QGSTREAMERVIDEOOVERLAY_P_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 -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QGstreamerSinkProperties; -class QGstreamerVideoOverlay - : public QObject - , public QGstreamerSyncMessageFilter - , public QGstreamerBusMessageFilter - , private QGstreamerBufferProbe -{ - Q_OBJECT - Q_INTERFACES(QGstreamerSyncMessageFilter QGstreamerBusMessageFilter) -public: - explicit QGstreamerVideoOverlay(QObject *parent = 0, const QByteArray &elementName = QByteArray()); - virtual ~QGstreamerVideoOverlay(); - - GstElement *videoSink() const; - void setVideoSink(GstElement *); - QSize nativeVideoSize() const; - - void setWindowHandle(WId id); - void expose(); - void setRenderRectangle(const QRect &rect); - - bool isActive() const; - - Qt::AspectRatioMode aspectRatioMode() const; - void setAspectRatioMode(Qt::AspectRatioMode mode); - - int brightness() const; - void setBrightness(int brightness); - - int contrast() const; - void setContrast(int contrast); - - int hue() const; - void setHue(int hue); - - int saturation() const; - void setSaturation(int saturation); - - bool processSyncMessage(const QGstreamerMessage &message) override; - bool processBusMessage(const QGstreamerMessage &message) override; - -Q_SIGNALS: - void nativeVideoSizeChanged(); - void activeChanged(); - void brightnessChanged(int brightness); - void contrastChanged(int contrast); - void hueChanged(int hue); - void saturationChanged(int saturation); - -private: - void setWindowHandle_helper(WId id); - void updateIsActive(); - void probeCaps(GstCaps *caps) override; - static void showPrerollFrameChanged(GObject *, GParamSpec *, QGstreamerVideoOverlay *); - - GstElement *m_videoSink = nullptr; - QSize m_nativeVideoSize; - bool m_isActive = false; - - QGstreamerSinkProperties *m_sinkProperties = nullptr; - WId m_windowId = 0; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERVIDEOOVERLAY_P_H - diff --git a/src/gsttools/qgstreamervideoprobecontrol.cpp b/src/gsttools/qgstreamervideoprobecontrol.cpp deleted file mode 100644 index 3d587eb2c..000000000 --- a/src/gsttools/qgstreamervideoprobecontrol.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreamervideoprobecontrol_p.h" - -#include "qgstutils_p.h" -#include - -QGstreamerVideoProbeControl::QGstreamerVideoProbeControl(QObject *parent) - : QMediaVideoProbeControl(parent) -{ -} - -QGstreamerVideoProbeControl::~QGstreamerVideoProbeControl() -{ -} - -void QGstreamerVideoProbeControl::startFlushing() -{ - m_flushing = true; - - { - QMutexLocker locker(&m_frameMutex); - m_pendingFrame = QVideoFrame(); - } - - // only emit flush if at least one frame was probed - if (m_frameProbed) - emit flush(); -} - -void QGstreamerVideoProbeControl::stopFlushing() -{ - m_flushing = false; -} - -void QGstreamerVideoProbeControl::probeCaps(GstCaps *caps) -{ - GstVideoInfo videoInfo; - QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &videoInfo); - - QMutexLocker locker(&m_frameMutex); - m_videoInfo = videoInfo; - m_format = format; -} - -bool QGstreamerVideoProbeControl::probeBuffer(GstBuffer *buffer) -{ - QMutexLocker locker(&m_frameMutex); - - if (m_flushing || !m_format.isValid()) - return true; - - QVideoFrame frame( - new QGstVideoBuffer(buffer, m_videoInfo), - m_format.frameSize(), - m_format.pixelFormat()); - - QGstUtils::setFrameTimeStamps(&frame, buffer); - - m_frameProbed = true; - - if (!m_pendingFrame.isValid()) - QMetaObject::invokeMethod(this, "frameProbed", Qt::QueuedConnection); - m_pendingFrame = frame; - - return true; -} - -void QGstreamerVideoProbeControl::frameProbed() -{ - QVideoFrame frame; - { - QMutexLocker locker(&m_frameMutex); - if (!m_pendingFrame.isValid()) - return; - frame = m_pendingFrame; - m_pendingFrame = QVideoFrame(); - } - emit videoFrameProbed(frame); -} diff --git a/src/gsttools/qgstreamervideoprobecontrol_p.h b/src/gsttools/qgstreamervideoprobecontrol_p.h deleted file mode 100644 index 2876f3124..000000000 --- a/src/gsttools/qgstreamervideoprobecontrol_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOPROBECONTROL_H -#define QGSTREAMERVIDEOPROBECONTROL_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 -#include -#include -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstreamerVideoProbeControl - : public QMediaVideoProbeControl - , public QGstreamerBufferProbe - , public QSharedData -{ - Q_OBJECT -public: - explicit QGstreamerVideoProbeControl(QObject *parent); - virtual ~QGstreamerVideoProbeControl(); - - void probeCaps(GstCaps *caps) override; - bool probeBuffer(GstBuffer *buffer) override; - - void startFlushing(); - void stopFlushing(); - -private slots: - void frameProbed(); - -private: - QVideoSurfaceFormat m_format; - QVideoFrame m_pendingFrame; - QMutex m_frameMutex; - GstVideoInfo m_videoInfo; - bool m_flushing = false; - bool m_frameProbed = false; // true if at least one frame was probed -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERVIDEOPROBECONTROL_H diff --git a/src/gsttools/qgstreamervideorenderer.cpp b/src/gsttools/qgstreamervideorenderer.cpp deleted file mode 100644 index c6ca935a4..000000000 --- a/src/gsttools/qgstreamervideorenderer.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreamervideorenderer_p.h" -#include -#include -#include -#include - -#include - -static inline void resetSink(GstElement *&element, GstElement *v = nullptr) -{ - if (element) - gst_object_unref(GST_OBJECT(element)); - - if (v) - qt_gst_object_ref_sink(GST_OBJECT(v)); - - element = v; -} - -QGstreamerVideoRenderer::QGstreamerVideoRenderer(QObject *parent) - : QVideoRendererControl(parent) -{ -} - -QGstreamerVideoRenderer::~QGstreamerVideoRenderer() -{ - resetSink(m_videoSink); -} - -void QGstreamerVideoRenderer::setVideoSink(GstElement *sink) -{ - if (!sink) - return; - - resetSink(m_videoSink, sink); - emit sinkChanged(); -} - -GstElement *QGstreamerVideoRenderer::videoSink() -{ - if (!m_videoSink && m_surface) { - auto sink = reinterpret_cast(QGstVideoRendererSink::createSink(m_surface)); - resetSink(m_videoSink, sink); - } - - return m_videoSink; -} - -void QGstreamerVideoRenderer::stopRenderer() -{ - if (m_surface) - m_surface->stop(); -} - -QAbstractVideoSurface *QGstreamerVideoRenderer::surface() const -{ - return m_surface; -} - -void QGstreamerVideoRenderer::setSurface(QAbstractVideoSurface *surface) -{ - if (m_surface != surface) { - resetSink(m_videoSink); - - 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 QGstreamerVideoRenderer::handleFormatChange() -{ - setVideoSink(nullptr); -} diff --git a/src/gsttools/qgstreamervideorenderer_p.h b/src/gsttools/qgstreamervideorenderer_p.h deleted file mode 100644 index df8c58da2..000000000 --- a/src/gsttools/qgstreamervideorenderer_p.h +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEORENDERER_H -#define QGSTREAMERVIDEORENDERER_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 -#include -#include -#include - -#include "qgstreamervideorendererinterface_p.h" - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstreamerVideoRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface -{ - Q_OBJECT - Q_INTERFACES(QGstreamerVideoRendererInterface) -public: - QGstreamerVideoRenderer(QObject *parent = 0); - virtual ~QGstreamerVideoRenderer(); - - QAbstractVideoSurface *surface() const override; - void setSurface(QAbstractVideoSurface *surface) override; - - GstElement *videoSink() override; - void setVideoSink(GstElement *) override; - - void stopRenderer() override; - bool isReady() const override { return m_surface != 0; } - -signals: - void sinkChanged(); - void readyChanged(bool); - -private slots: - void handleFormatChange(); - -private: - GstElement *m_videoSink = nullptr; - QPointer m_surface; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERVIDEORENDRER_H diff --git a/src/gsttools/qgstreamervideorendererinterface.cpp b/src/gsttools/qgstreamervideorendererinterface.cpp deleted file mode 100644 index ae7de06f1..000000000 --- a/src/gsttools/qgstreamervideorendererinterface.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreamervideorendererinterface_p.h" - -QGstreamerVideoRendererInterface::~QGstreamerVideoRendererInterface() -{ -} diff --git a/src/gsttools/qgstreamervideorendererinterface_p.h b/src/gsttools/qgstreamervideorendererinterface_p.h deleted file mode 100644 index 231c843db..000000000 --- a/src/gsttools/qgstreamervideorendererinterface_p.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOOUTPUTCONTROL_H -#define QGSTREAMERVIDEOOUTPUTCONTROL_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 - -#include - -QT_BEGIN_NAMESPACE - -class QGstreamerVideoRendererInterface -{ -public: - virtual ~QGstreamerVideoRendererInterface(); - virtual GstElement *videoSink() = 0; - virtual void setVideoSink(GstElement *) {}; - - //stopRenderer() is called when the renderer element is stopped. - //it can be reimplemented when video renderer can't detect - //changes to NULL state but has to free video resources. - virtual void stopRenderer() {} - - //the video output is configured, usually after the first paint event - //(winId is known, - virtual bool isReady() const { return true; } - - //signals: - //void sinkChanged(); - //void readyChanged(bool); -}; - -#define QGstreamerVideoRendererInterface_iid "org.qt-project.qt.gstreamervideorenderer/5.0" -Q_DECLARE_INTERFACE(QGstreamerVideoRendererInterface, QGstreamerVideoRendererInterface_iid) -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstreamervideowidget.cpp b/src/gsttools/qgstreamervideowidget.cpp deleted file mode 100644 index 4137aff32..000000000 --- a/src/gsttools/qgstreamervideowidget.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreamervideowidget_p.h" -#include "qgstutils_p.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QGstreamerVideoWidget : public QWidget -{ -public: - QGstreamerVideoWidget(QWidget *parent = 0) - :QWidget(parent) - { - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - QPalette palette; - palette.setColor(QPalette::Window, Qt::black); - setPalette(palette); - } - - virtual ~QGstreamerVideoWidget() {} - - QSize sizeHint() const override - { - return m_nativeSize; - } - - void setNativeSize( const QSize &size) - { - if (size != m_nativeSize) { - m_nativeSize = size; - if (size.isEmpty()) - setMinimumSize(0,0); - else - setMinimumSize(160,120); - - updateGeometry(); - } - } - - void paint_helper() - { - QPainter painter(this); - painter.fillRect(rect(), palette().window()); - } - -protected: - void paintEvent(QPaintEvent *) override - { - paint_helper(); - } - - QSize m_nativeSize; -}; - -QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *parent, const QByteArray &elementName) - : QVideoWidgetControl(parent) - , m_videoOverlay(this, !elementName.isEmpty() ? elementName : qgetenv("QT_GSTREAMER_WIDGET_VIDEOSINK")) -{ - connect(&m_videoOverlay, &QGstreamerVideoOverlay::activeChanged, - this, &QGstreamerVideoWidgetControl::onOverlayActiveChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::nativeVideoSizeChanged, - this, &QGstreamerVideoWidgetControl::onNativeVideoSizeChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::brightnessChanged, - this, &QGstreamerVideoWidgetControl::brightnessChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::contrastChanged, - this, &QGstreamerVideoWidgetControl::contrastChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::hueChanged, - this, &QGstreamerVideoWidgetControl::hueChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::saturationChanged, - this, &QGstreamerVideoWidgetControl::saturationChanged); -} - -QGstreamerVideoWidgetControl::~QGstreamerVideoWidgetControl() -{ - delete m_widget; -} - -void QGstreamerVideoWidgetControl::createVideoWidget() -{ - if (m_widget) - return; - - m_widget = new QGstreamerVideoWidget; - - m_widget->installEventFilter(this); - m_videoOverlay.setWindowHandle(m_windowId = m_widget->winId()); -} - -GstElement *QGstreamerVideoWidgetControl::videoSink() -{ - return m_videoOverlay.videoSink(); -} - -void QGstreamerVideoWidgetControl::setVideoSink(GstElement *sink) -{ - m_videoOverlay.setVideoSink(sink); -} - -void QGstreamerVideoWidgetControl::onOverlayActiveChanged() -{ - updateWidgetAttributes(); -} - -void QGstreamerVideoWidgetControl::stopRenderer() -{ - m_stopped = true; - updateWidgetAttributes(); - m_widget->setNativeSize(QSize()); -} - -void QGstreamerVideoWidgetControl::onNativeVideoSizeChanged() -{ - const QSize &size = m_videoOverlay.nativeVideoSize(); - - if (size.isValid()) - m_stopped = false; - - if (m_widget) - m_widget->setNativeSize(size); -} - -bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e) -{ - if (m_widget && object == m_widget) { - if (e->type() == QEvent::ParentChange || e->type() == QEvent::Show || e->type() == QEvent::WinIdChange) { - WId newWId = m_widget->winId(); - if (newWId != m_windowId) - m_videoOverlay.setWindowHandle(m_windowId = newWId); - } - - if (e->type() == QEvent::Paint) { - // Update overlay by new size if any. - if (QGstUtils::useOpenGL()) - 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 - m_widget->paint_helper(); // paints the black background - - return true; - } - } - - return false; -} - -void QGstreamerVideoWidgetControl::updateWidgetAttributes() -{ - // When frames are being rendered (sink is active), we need the WA_PaintOnScreen attribute to - // be set in order to avoid flickering when the widget is repainted (for example when resized). - // We need to clear that flag when the the sink is inactive to allow the widget to paint its - // background, otherwise some garbage will be displayed. - if (m_videoOverlay.isActive() && !m_stopped) { - m_widget->setAttribute(Qt::WA_NoSystemBackground, true); - m_widget->setAttribute(Qt::WA_PaintOnScreen, true); - } else { - m_widget->setAttribute(Qt::WA_NoSystemBackground, false); - m_widget->setAttribute(Qt::WA_PaintOnScreen, false); - m_widget->update(); - } -} - -bool QGstreamerVideoWidgetControl::processSyncMessage(const QGstreamerMessage &message) -{ - return m_videoOverlay.processSyncMessage(message); -} - -bool QGstreamerVideoWidgetControl::processBusMessage(const QGstreamerMessage &message) -{ - return m_videoOverlay.processBusMessage(message); -} - -QWidget *QGstreamerVideoWidgetControl::videoWidget() -{ - createVideoWidget(); - return m_widget; -} - -Qt::AspectRatioMode QGstreamerVideoWidgetControl::aspectRatioMode() const -{ - return m_videoOverlay.aspectRatioMode(); -} - -void QGstreamerVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_videoOverlay.setAspectRatioMode(mode); -} - -bool QGstreamerVideoWidgetControl::isFullScreen() const -{ - return m_fullScreen; -} - -void QGstreamerVideoWidgetControl::setFullScreen(bool fullScreen) -{ - emit fullScreenChanged(m_fullScreen = fullScreen); -} - -int QGstreamerVideoWidgetControl::brightness() const -{ - return m_videoOverlay.brightness(); -} - -void QGstreamerVideoWidgetControl::setBrightness(int brightness) -{ - m_videoOverlay.setBrightness(brightness); -} - -int QGstreamerVideoWidgetControl::contrast() const -{ - return m_videoOverlay.contrast(); -} - -void QGstreamerVideoWidgetControl::setContrast(int contrast) -{ - m_videoOverlay.setContrast(contrast); -} - -int QGstreamerVideoWidgetControl::hue() const -{ - return m_videoOverlay.hue(); -} - -void QGstreamerVideoWidgetControl::setHue(int hue) -{ - m_videoOverlay.setHue(hue); -} - -int QGstreamerVideoWidgetControl::saturation() const -{ - return m_videoOverlay.saturation(); -} - -void QGstreamerVideoWidgetControl::setSaturation(int saturation) -{ - m_videoOverlay.setSaturation(saturation); -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstreamervideowidget_p.h b/src/gsttools/qgstreamervideowidget_p.h deleted file mode 100644 index 6eec6ae52..000000000 --- a/src/gsttools/qgstreamervideowidget_p.h +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOWIDGET_H -#define QGSTREAMERVIDEOWIDGET_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 -#include - -#include "qgstreamervideorendererinterface_p.h" -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstreamerVideoWidget; - -class Q_GSTTOOLS_EXPORT QGstreamerVideoWidgetControl - : public QVideoWidgetControl - , public QGstreamerVideoRendererInterface - , public QGstreamerSyncMessageFilter - , public QGstreamerBusMessageFilter -{ - Q_OBJECT - Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter QGstreamerBusMessageFilter) -public: - explicit QGstreamerVideoWidgetControl(QObject *parent = 0, const QByteArray &elementName = QByteArray()); - virtual ~QGstreamerVideoWidgetControl(); - - GstElement *videoSink() override; - void setVideoSink(GstElement *) override; - - QWidget *videoWidget() override; - - void stopRenderer() override; - - Qt::AspectRatioMode aspectRatioMode() const override; - void setAspectRatioMode(Qt::AspectRatioMode mode) override; - - bool isFullScreen() const override; - void setFullScreen(bool fullScreen) override; - - int brightness() const override; - void setBrightness(int brightness) override; - - int contrast() const override; - void setContrast(int contrast) override; - - int hue() const override; - void setHue(int hue) override; - - int saturation() const override; - void setSaturation(int saturation) override; - - bool eventFilter(QObject *object, QEvent *event) override; - -signals: - void sinkChanged(); - void readyChanged(bool); - -private Q_SLOTS: - void onOverlayActiveChanged(); - void onNativeVideoSizeChanged(); - -private: - void createVideoWidget(); - void updateWidgetAttributes(); - - bool processSyncMessage(const QGstreamerMessage &message) override; - bool processBusMessage(const QGstreamerMessage &message) override; - - QGstreamerVideoOverlay m_videoOverlay; - QGstreamerVideoWidget *m_widget = nullptr; - bool m_stopped = false; - WId m_windowId = 0; - bool m_fullScreen = false; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERVIDEOWIDGET_H diff --git a/src/gsttools/qgstreamervideowindow.cpp b/src/gsttools/qgstreamervideowindow.cpp deleted file mode 100644 index e7e3c5044..000000000 --- a/src/gsttools/qgstreamervideowindow.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstreamervideowindow_p.h" -#include - -#include - -QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const QByteArray &elementName) - : QVideoWindowControl(parent) - , m_videoOverlay(this, !elementName.isEmpty() ? elementName : qgetenv("QT_GSTREAMER_WINDOW_VIDEOSINK")) -{ - connect(&m_videoOverlay, &QGstreamerVideoOverlay::nativeVideoSizeChanged, - this, &QGstreamerVideoWindow::nativeSizeChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::brightnessChanged, - this, &QGstreamerVideoWindow::brightnessChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::contrastChanged, - this, &QGstreamerVideoWindow::contrastChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::hueChanged, - this, &QGstreamerVideoWindow::hueChanged); - connect(&m_videoOverlay, &QGstreamerVideoOverlay::saturationChanged, - this, &QGstreamerVideoWindow::saturationChanged); -} - -QGstreamerVideoWindow::~QGstreamerVideoWindow() -{ -} - -GstElement *QGstreamerVideoWindow::videoSink() -{ - return m_videoOverlay.videoSink(); -} - -WId QGstreamerVideoWindow::winId() const -{ - return m_windowId; -} - -void QGstreamerVideoWindow::setWinId(WId id) -{ - if (m_windowId == id) - return; - - WId oldId = m_windowId; - m_videoOverlay.setWindowHandle(m_windowId = id); - - if (!oldId) - emit readyChanged(true); - - if (!id) - emit readyChanged(false); -} - -bool QGstreamerVideoWindow::processSyncMessage(const QGstreamerMessage &message) -{ - return m_videoOverlay.processSyncMessage(message); -} - -bool QGstreamerVideoWindow::processBusMessage(const QGstreamerMessage &message) -{ - return m_videoOverlay.processBusMessage(message); -} - -QRect QGstreamerVideoWindow::displayRect() const -{ - return m_displayRect; -} - -void QGstreamerVideoWindow::setDisplayRect(const QRect &rect) -{ - m_videoOverlay.setRenderRectangle(m_displayRect = rect); - repaint(); -} - -Qt::AspectRatioMode QGstreamerVideoWindow::aspectRatioMode() const -{ - return m_videoOverlay.aspectRatioMode(); -} - -void QGstreamerVideoWindow::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_videoOverlay.setAspectRatioMode(mode); -} - -void QGstreamerVideoWindow::repaint() -{ - m_videoOverlay.expose(); -} - -int QGstreamerVideoWindow::brightness() const -{ - return m_videoOverlay.brightness(); -} - -void QGstreamerVideoWindow::setBrightness(int brightness) -{ - m_videoOverlay.setBrightness(brightness); -} - -int QGstreamerVideoWindow::contrast() const -{ - return m_videoOverlay.contrast(); -} - -void QGstreamerVideoWindow::setContrast(int contrast) -{ - m_videoOverlay.setContrast(contrast); -} - -int QGstreamerVideoWindow::hue() const -{ - return m_videoOverlay.hue(); -} - -void QGstreamerVideoWindow::setHue(int hue) -{ - m_videoOverlay.setHue(hue); -} - -int QGstreamerVideoWindow::saturation() const -{ - return m_videoOverlay.saturation(); -} - -void QGstreamerVideoWindow::setSaturation(int saturation) -{ - m_videoOverlay.setSaturation(saturation); -} - -bool QGstreamerVideoWindow::isFullScreen() const -{ - return m_fullScreen; -} - -void QGstreamerVideoWindow::setFullScreen(bool fullScreen) -{ - emit fullScreenChanged(m_fullScreen = fullScreen); -} - -QSize QGstreamerVideoWindow::nativeSize() const -{ - return m_videoOverlay.nativeVideoSize(); -} diff --git a/src/gsttools/qgstreamervideowindow_p.h b/src/gsttools/qgstreamervideowindow_p.h deleted file mode 100644 index a0ed8599b..000000000 --- a/src/gsttools/qgstreamervideowindow_p.h +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOWINDOW_H -#define QGSTREAMERVIDEOWINDOW_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 -#include - -#include "qgstreamervideorendererinterface_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QAbstractVideoSurface; - -class Q_GSTTOOLS_EXPORT QGstreamerVideoWindow : - public QVideoWindowControl, - public QGstreamerVideoRendererInterface, - public QGstreamerSyncMessageFilter, - public QGstreamerBusMessageFilter -{ - Q_OBJECT - Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter QGstreamerBusMessageFilter) -public: - explicit QGstreamerVideoWindow(QObject *parent = 0, const QByteArray &elementName = QByteArray()); - ~QGstreamerVideoWindow(); - - WId winId() const override; - void setWinId(WId id) override; - - QRect displayRect() const override; - void setDisplayRect(const QRect &rect) override; - - bool isFullScreen() const override; - void setFullScreen(bool fullScreen) override; - - QSize nativeSize() const override; - - Qt::AspectRatioMode aspectRatioMode() const override; - void setAspectRatioMode(Qt::AspectRatioMode mode) override; - - void repaint() override; - - int brightness() const override; - void setBrightness(int brightness) override; - - int contrast() const override; - void setContrast(int contrast) override; - - int hue() const override; - void setHue(int hue) override; - - int saturation() const override; - void setSaturation(int saturation) override; - - QAbstractVideoSurface *surface() const; - - GstElement *videoSink() override; - - bool processSyncMessage(const QGstreamerMessage &message) override; - bool processBusMessage(const QGstreamerMessage &message) override; - bool isReady() const override { return m_windowId != 0; } - -signals: - void sinkChanged(); - void readyChanged(bool); - -private: - QGstreamerVideoOverlay m_videoOverlay; - WId m_windowId = 0; - QRect m_displayRect; - bool m_fullScreen = false; - mutable QColor m_colorKey = QColor::Invalid; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgsttools_global_p.h b/src/gsttools/qgsttools_global_p.h deleted file mode 100644 index babcd3aaf..000000000 --- a/src/gsttools/qgsttools_global_p.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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$ -** -****************************************************************************/ - -#ifndef QGSTTOOLS_GLOBAL_H -#define QGSTTOOLS_GLOBAL_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 - -QT_BEGIN_NAMESPACE - -#ifndef QT_STATIC -# if defined(QT_BUILD_MULTIMEDIAGSTTOOLS_LIB) -# define Q_GSTTOOLS_EXPORT Q_DECL_EXPORT -# else -# define Q_GSTTOOLS_EXPORT Q_DECL_IMPORT -# endif -#else -# define Q_GSTTOOLS_EXPORT -#endif - -QT_END_NAMESPACE - -#endif // QGSTTOOLS_GLOBAL_H diff --git a/src/gsttools/qgstutils.cpp b/src/gsttools/qgstutils.cpp deleted file mode 100644 index 8891aad87..000000000 --- a/src/gsttools/qgstutils.cpp +++ /dev/null @@ -1,1150 +0,0 @@ -/**************************************************************************** -** -** 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 -#include "qgstutils_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -template static int lengthOf(const T (&)[N]) { return N; } - -#include "qgstreamervideoinputdevicecontrol_p.h" - -QT_BEGIN_NAMESPACE - -//internal -static void addTagToMap(const GstTagList *list, - const gchar *tag, - gpointer user_data) -{ - QMap *map = reinterpret_cast* >(user_data); - - GValue val; - val.g_type = 0; - gst_tag_list_copy_value(&val,list,tag); - - switch( G_VALUE_TYPE(&val) ) { - case G_TYPE_STRING: - { - const gchar *str_value = g_value_get_string(&val); - map->insert(QByteArray(tag), QString::fromUtf8(str_value)); - break; - } - case G_TYPE_INT: - map->insert(QByteArray(tag), g_value_get_int(&val)); - break; - case G_TYPE_UINT: - map->insert(QByteArray(tag), g_value_get_uint(&val)); - break; - case G_TYPE_LONG: - map->insert(QByteArray(tag), qint64(g_value_get_long(&val))); - break; - case G_TYPE_BOOLEAN: - map->insert(QByteArray(tag), g_value_get_boolean(&val)); - break; - case G_TYPE_CHAR: -#if GLIB_CHECK_VERSION(2,32,0) - map->insert(QByteArray(tag), g_value_get_schar(&val)); -#else - map->insert(QByteArray(tag), g_value_get_char(&val)); -#endif - break; - case G_TYPE_DOUBLE: - map->insert(QByteArray(tag), g_value_get_double(&val)); - break; - default: - // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch - if (G_VALUE_TYPE(&val) == G_TYPE_DATE) { - const GDate *date = (const GDate *)g_value_get_boxed(&val); - if (g_date_valid(date)) { - int year = g_date_get_year(date); - int month = g_date_get_month(date); - int day = g_date_get_day(date); - map->insert(QByteArray(tag), QDate(year,month,day)); - if (!map->contains("year")) - map->insert("year", year); - } - } else if (G_VALUE_TYPE(&val) == GST_TYPE_DATE_TIME) { - const GstDateTime *dateTime = (const GstDateTime *)g_value_get_boxed(&val); - int year = gst_date_time_has_year(dateTime) ? gst_date_time_get_year(dateTime) : 0; - int month = gst_date_time_has_month(dateTime) ? gst_date_time_get_month(dateTime) : 0; - int day = gst_date_time_has_day(dateTime) ? gst_date_time_get_day(dateTime) : 0; - if (gst_date_time_has_time(dateTime)) { - int hour = gst_date_time_get_hour(dateTime); - int minute = gst_date_time_get_minute(dateTime); - int second = gst_date_time_get_second(dateTime); - float tz = gst_date_time_get_time_zone_offset(dateTime); - QDateTime dateTime(QDate(year, month, day), QTime(hour, minute, second), - Qt::OffsetFromUTC, tz * 60 * 60); - map->insert(QByteArray(tag), dateTime); - } else if (year > 0 && month > 0 && day > 0) { - map->insert(QByteArray(tag), QDate(year,month,day)); - } - if (!map->contains("year") && year > 0) - map->insert("year", year); - } else if (G_VALUE_TYPE(&val) == GST_TYPE_SAMPLE) { - GstSample *sample = (GstSample *)g_value_get_boxed(&val); - GstCaps* caps = gst_sample_get_caps(sample); - if (caps && !gst_caps_is_empty(caps)) { - GstStructure *structure = gst_caps_get_structure(caps, 0); - const gchar *name = gst_structure_get_name(structure); - if (QByteArray(name).startsWith("image/")) { - GstBuffer *buffer = gst_sample_get_buffer(sample); - if (buffer) { - GstMapInfo info; - gst_buffer_map(buffer, &info, GST_MAP_READ); - map->insert(QByteArray(tag), QImage::fromData(info.data, info.size, name)); - gst_buffer_unmap(buffer, &info); - } - } - } - } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) { - int nom = gst_value_get_fraction_numerator(&val); - int denom = gst_value_get_fraction_denominator(&val); - - if (denom > 0) { - map->insert(QByteArray(tag), double(nom)/denom); - } - } - break; - } - - g_value_unset(&val); -} - -/*! - \class QGstUtils - \internal -*/ - -/*! - Convert GstTagList structure to QMap. - - Mapping to int, bool, char, string, fractions and date are supported. - Fraction values are converted to doubles. -*/ -QMap QGstUtils::gstTagListToMap(const GstTagList *tags) -{ - QMap res; - gst_tag_list_foreach(tags, addTagToMap, &res); - - return res; -} - -/*! - Returns resolution of \a caps. - If caps doesn't have a valid size, an empty QSize is returned. -*/ -QSize QGstUtils::capsResolution(const GstCaps *caps) -{ - if (gst_caps_get_size(caps) == 0) - return QSize(); - - return structureResolution(gst_caps_get_structure(caps, 0)); -} - -/*! - Returns aspect ratio corrected resolution of \a caps. - If caps doesn't have a valid size, an empty QSize is returned. -*/ -QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) -{ - QSize size; - - if (caps) { - size = capsResolution(caps); - - gint aspectNum = 0; - gint aspectDenum = 0; - if (!size.isEmpty() && gst_structure_get_fraction( - gst_caps_get_structure(caps, 0), "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { - if (aspectDenum > 0) - size.setWidth(size.width()*aspectNum/aspectDenum); - } - } - - return size; -} - - -namespace { - -struct AudioFormat -{ - GstAudioFormat format; - QAudioFormat::SampleType sampleType; - QAudioFormat::Endian byteOrder; - int sampleSize; -}; -static const AudioFormat qt_audioLookup[] = -{ - { GST_AUDIO_FORMAT_S8 , QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 8 }, - { GST_AUDIO_FORMAT_U8 , QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 8 }, - { GST_AUDIO_FORMAT_S16LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 16 }, - { GST_AUDIO_FORMAT_S16BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 16 }, - { GST_AUDIO_FORMAT_U16LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 16 }, - { GST_AUDIO_FORMAT_U16BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 16 }, - { GST_AUDIO_FORMAT_S32LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 32 }, - { GST_AUDIO_FORMAT_S32BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 32 }, - { GST_AUDIO_FORMAT_U32LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 32 }, - { GST_AUDIO_FORMAT_U32BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 32 }, - { GST_AUDIO_FORMAT_S24LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 24 }, - { GST_AUDIO_FORMAT_S24BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 24 }, - { GST_AUDIO_FORMAT_U24LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 24 }, - { GST_AUDIO_FORMAT_U24BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 24 }, - { GST_AUDIO_FORMAT_F32LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 32 }, - { GST_AUDIO_FORMAT_F32BE, QAudioFormat::Float , QAudioFormat::BigEndian , 32 }, - { GST_AUDIO_FORMAT_F64LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 64 }, - { GST_AUDIO_FORMAT_F64BE, QAudioFormat::Float , QAudioFormat::BigEndian , 64 } -}; - -} - -/*! - Returns audio format for caps. - If caps doesn't have a valid audio format, an empty QAudioFormat is returned. -*/ - -QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps) -{ - QAudioFormat format; - GstAudioInfo info; - if (gst_audio_info_from_caps(&info, caps)) { - for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { - if (qt_audioLookup[i].format != info.finfo->format) - continue; - - format.setSampleType(qt_audioLookup[i].sampleType); - format.setByteOrder(qt_audioLookup[i].byteOrder); - format.setSampleSize(qt_audioLookup[i].sampleSize); - format.setSampleRate(info.rate); - format.setChannelCount(info.channels); - format.setCodec(QStringLiteral("audio/pcm")); - - return format; - } - } - - return format; -} - -/* - Returns audio format for a sample. - If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. -*/ -QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample) -{ - GstCaps* caps = gst_sample_get_caps(sample); - if (!caps) - return QAudioFormat(); - - return QGstUtils::audioFormatForCaps(caps); -} - -/*! - Builds GstCaps for an audio format. - Returns 0 if the audio format is not valid. - Caller must unref GstCaps. -*/ - -GstCaps *QGstUtils::capsForAudioFormat(const QAudioFormat &format) -{ - if (!format.isValid()) - return 0; - - const QAudioFormat::SampleType sampleType = format.sampleType(); - const QAudioFormat::Endian byteOrder = format.byteOrder(); - const int sampleSize = format.sampleSize(); - - for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { - if (qt_audioLookup[i].sampleType != sampleType - || qt_audioLookup[i].byteOrder != byteOrder - || qt_audioLookup[i].sampleSize != sampleSize) { - continue; - } - - return gst_caps_new_simple( - "audio/x-raw", - "format" , G_TYPE_STRING, gst_audio_format_to_string(qt_audioLookup[i].format), - "rate" , G_TYPE_INT , format.sampleRate(), - "channels", G_TYPE_INT , format.channelCount(), - nullptr); - } - return 0; -} - -static QSet m_videoSources; -static QSet m_audioSources; -static QSet m_audioSinks; - -static void addDevice(GstDevice *device) -{ - gchar *type = gst_device_get_device_class(device); -// qDebug() << "adding device:" << device << type << gst_device_get_display_name(device); - gst_object_ref(device); - if (!strcmp(type, "Video/Source")) - m_videoSources.insert(device); - else if (!strcmp(type, "Audio/Source")) - m_audioSources.insert(device); - else if (!strcmp(type, "Audio/Sink")) - m_audioSinks.insert(device); - else - gst_object_unref(device); - g_free(type); -} - -static void removeDevice(GstDevice *device) -{ -// qDebug() << "removing device:" << device << gst_device_get_display_name(device); - if (m_videoSources.remove(device) || - m_audioSources.remove(device) || - m_audioSinks.remove(device)) - gst_object_unref(device); -} - -static gboolean deviceMonitor(GstBus *, GstMessage *message, gpointer) -{ - GstDevice *device = nullptr; - - switch (GST_MESSAGE_TYPE (message)) { - case GST_MESSAGE_DEVICE_ADDED: - gst_message_parse_device_added (message, &device); - addDevice(device); - break; - case GST_MESSAGE_DEVICE_REMOVED: - gst_message_parse_device_removed (message, &device); - removeDevice(device); - break; - default: - break; - } - if (device) - gst_object_unref (device); - - return G_SOURCE_CONTINUE; -} - -void setupDeviceMonitor() -{ - GstDeviceMonitor *monitor; - GstBus *bus; - - monitor = gst_device_monitor_new(); - - bus = gst_device_monitor_get_bus(monitor); - gst_bus_add_watch(bus, deviceMonitor, NULL); - gst_object_unref(bus); - - gst_device_monitor_add_filter (monitor, "Video/Source", NULL); - gst_device_monitor_add_filter (monitor, "Audio/Source", NULL); - gst_device_monitor_add_filter (monitor, "Audio/Sink", NULL); - - auto devices = gst_device_monitor_get_devices(monitor); - - while (devices) { - GstDevice *device = static_cast(devices->data); - addDevice(device); - gst_object_unref(device); - devices = g_list_delete_link(devices, devices); - } - - gst_device_monitor_start(monitor); -} - - -void QGstUtils::initializeGst() -{ - static bool initialized = false; - if (!initialized) { - initialized = true; - gst_init(nullptr, nullptr); - setupDeviceMonitor(); - } -} - -namespace { - const char* getCodecAlias(const QString &codec) - { - if (codec.startsWith(QLatin1String("avc1."))) - return "video/x-h264"; - - if (codec.startsWith(QLatin1String("mp4a."))) - return "audio/mpeg4"; - - if (codec.startsWith(QLatin1String("mp4v.20."))) - return "video/mpeg4"; - - if (codec == QLatin1String("samr")) - return "audio/amr"; - - return 0; - } - - const char* getMimeTypeAlias(const QString &mimeType) - { - if (mimeType == QLatin1String("video/mp4")) - return "video/mpeg4"; - - if (mimeType == QLatin1String("audio/mp4")) - return "audio/mpeg4"; - - if (mimeType == QLatin1String("video/ogg") - || mimeType == QLatin1String("audio/ogg")) - return "application/ogg"; - - return 0; - } -} - -QMultimedia::SupportEstimate QGstUtils::hasSupport(const QString &mimeType, - const QStringList &codecs, - const QSet &supportedMimeTypeSet) -{ - if (supportedMimeTypeSet.isEmpty()) - return QMultimedia::NotSupported; - - QString mimeTypeLowcase = mimeType.toLower(); - bool containsMimeType = supportedMimeTypeSet.contains(mimeTypeLowcase); - if (!containsMimeType) { - const char* mimeTypeAlias = getMimeTypeAlias(mimeTypeLowcase); - containsMimeType = supportedMimeTypeSet.contains(QLatin1String(mimeTypeAlias)); - if (!containsMimeType) { - containsMimeType = supportedMimeTypeSet.contains(QLatin1String("video/") + mimeTypeLowcase) - || supportedMimeTypeSet.contains(QLatin1String("video/x-") + mimeTypeLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/") + mimeTypeLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/x-") + mimeTypeLowcase); - } - } - - int supportedCodecCount = 0; - for (const QString &codec : codecs) { - QString codecLowcase = codec.toLower(); - const char* codecAlias = getCodecAlias(codecLowcase); - if (codecAlias) { - if (supportedMimeTypeSet.contains(QLatin1String(codecAlias))) - supportedCodecCount++; - } else if (supportedMimeTypeSet.contains(QLatin1String("video/") + codecLowcase) - || supportedMimeTypeSet.contains(QLatin1String("video/x-") + codecLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/") + codecLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/x-") + codecLowcase)) { - supportedCodecCount++; - } - } - if (supportedCodecCount > 0 && supportedCodecCount == codecs.size()) - return QMultimedia::ProbablySupported; - - if (supportedCodecCount == 0 && !containsMimeType) - return QMultimedia::NotSupported; - - return QMultimedia::MaybeSupported; -} - -QList QGstUtils::enumerateCameras() -{ - initializeGst(); - - QList devices; - - for (auto *d : qAsConst(m_videoSources)) { - auto *properties = gst_device_get_properties(d); - if (properties) { - CameraInfo info; - auto *desc = gst_device_get_display_name(d); - info.description = QString::fromUtf8(desc); - g_free(desc); - - auto *name = gst_structure_get_string(properties, "device.path"); - info.name = QString::fromUtf8(name); - info.driver = gst_structure_get_string(properties, "v4l2.device.driver"); - info.orientation = 0; - info.position = QCamera::UnspecifiedPosition; - gst_structure_free(properties); - - devices.append(info); - } - } - return devices; -} - -QList QGstUtils::cameraDevices() -{ - QList devices; - - const auto cameras = enumerateCameras(); - devices.reserve(cameras.size()); - for (const CameraInfo &camera : cameras) - devices.append(camera.name.toUtf8()); - - return devices; -} - -QString QGstUtils::cameraDescription(const QString &device) -{ - const auto cameras = enumerateCameras(); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.description; - } - return QString(); -} - -QCamera::Position QGstUtils::cameraPosition(const QString &device) -{ - const auto cameras = enumerateCameras(); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.position; - } - return QCamera::UnspecifiedPosition; -} - -int QGstUtils::cameraOrientation(const QString &device) -{ - const auto cameras = enumerateCameras(); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.orientation; - } - return 0; -} - -QByteArray QGstUtils::cameraDriver(const QString &device) -{ - const auto cameras = enumerateCameras(); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.driver; - } - return QByteArray(); -} - - -const QSet &QGstUtils::audioSources() -{ - return m_audioSources; -} - -const QSet &QGstUtils::audioSinks() -{ - return m_audioSinks; -} - -QSet QGstUtils::supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)) -{ - QSet supportedMimeTypes; - - //enumerate supported mime types - gst_init(nullptr, nullptr); - - GstRegistry *registry = gst_registry_get(); - GList *orig_plugins = gst_registry_get_plugin_list(registry); - for (GList *plugins = orig_plugins; plugins; plugins = g_list_next(plugins)) { - GstPlugin *plugin = (GstPlugin *) (plugins->data); - if (GST_OBJECT_FLAG_IS_SET(GST_OBJECT(plugin), GST_PLUGIN_FLAG_BLACKLISTED)) - continue; - - GList *orig_features = gst_registry_get_feature_list_by_plugin( - registry, gst_plugin_get_name(plugin)); - for (GList *features = orig_features; features; features = g_list_next(features)) { - if (G_UNLIKELY(features->data == nullptr)) - continue; - - GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); - GstElementFactory *factory; - - if (GST_IS_TYPE_FIND_FACTORY(feature)) { - QString name(QLatin1String(gst_plugin_feature_get_name(feature))); - if (name.contains(QLatin1Char('/'))) //filter out any string without '/' which is obviously not a mime type - supportedMimeTypes.insert(name.toLower()); - continue; - } else if (!GST_IS_ELEMENT_FACTORY (feature) - || !(factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)))) { - continue; - } else if (!isValidFactory(factory)) { - // Do nothing - } else for (const GList *pads = gst_element_factory_get_static_pad_templates(factory); - pads; - pads = g_list_next(pads)) { - GstStaticPadTemplate *padtemplate = static_cast(pads->data); - - if (padtemplate->direction == GST_PAD_SINK && padtemplate->static_caps.string) { - GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); - if (gst_caps_is_any(caps) || gst_caps_is_empty(caps)) { - } else for (guint i = 0; i < gst_caps_get_size(caps); i++) { - GstStructure *structure = gst_caps_get_structure(caps, i); - QString nameLowcase = QString::fromLatin1(gst_structure_get_name(structure)).toLower(); - - supportedMimeTypes.insert(nameLowcase); - if (nameLowcase.contains(QLatin1String("mpeg"))) { - //Because mpeg version number is only included in the detail - //description, it is necessary to manually extract this information - //in order to match the mime type of mpeg4. - const GValue *value = gst_structure_get_value(structure, "mpegversion"); - if (value) { - gchar *str = gst_value_serialize(value); - QString versions = QLatin1String(str); - const QStringList elements = versions.split(QRegularExpression(QLatin1String("\\D+")), Qt::SkipEmptyParts); - for (const QString &e : elements) - supportedMimeTypes.insert(nameLowcase + e); - g_free(str); - } - } - } - } - } - gst_object_unref(factory); - } - gst_plugin_feature_list_free(orig_features); - } - gst_plugin_list_free (orig_plugins); - -#if defined QT_SUPPORTEDMIMETYPES_DEBUG - QStringList list = supportedMimeTypes.toList(); - list.sort(); - if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { - for (const QString &type : qAsConst(list)) - qDebug() << type; - } -#endif - return supportedMimeTypes; -} - -namespace { - -struct ColorFormat { QImage::Format imageFormat; GstVideoFormat gstFormat; }; -static const ColorFormat qt_colorLookup[] = -{ - { QImage::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx }, - { QImage::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA }, - { QImage::Format_RGB888 , GST_VIDEO_FORMAT_RGB }, - { QImage::Format_RGB16 , GST_VIDEO_FORMAT_RGB16 } -}; - -} - -QImage QGstUtils::bufferToImage(GstBuffer *buffer, const GstVideoInfo &videoInfo) -{ - QImage img; - - GstVideoInfo info = videoInfo; - GstVideoFrame frame; - if (!gst_video_frame_map(&frame, &info, buffer, GST_MAP_READ)) - return img; - - if (videoInfo.finfo->format == GST_VIDEO_FORMAT_I420) { - const int width = videoInfo.width; - const int height = videoInfo.height; - - const int stride[] = { frame.info.stride[0], frame.info.stride[1], frame.info.stride[2] }; - const uchar *data[] = { - static_cast(frame.data[0]), - static_cast(frame.data[1]), - static_cast(frame.data[2]) - }; - img = QImage(width/2, height/2, QImage::Format_RGB32); - - for (int y=0; yformat) - continue; - - const QImage image( - static_cast(frame.data[0]), - videoInfo.width, - videoInfo.height, - frame.info.stride[0], - qt_colorLookup[i].imageFormat); - img = image; - img.detach(); - - break; - } - - gst_video_frame_unmap(&frame); - - return img; -} - - -namespace { - -struct VideoFormat -{ - QVideoFrame::PixelFormat pixelFormat; - GstVideoFormat gstFormat; -}; - -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 }, - { QVideoFrame::Format_NV12 , GST_VIDEO_FORMAT_NV12 }, - { QVideoFrame::Format_NV21 , GST_VIDEO_FORMAT_NV21 }, - { QVideoFrame::Format_AYUV444, GST_VIDEO_FORMAT_AYUV }, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - { 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 }, - { QVideoFrame::Format_BGR24 , GST_VIDEO_FORMAT_BGR }, - { QVideoFrame::Format_RGB565, GST_VIDEO_FORMAT_RGB16 } -}; - -static int indexOfVideoFormat(QVideoFrame::PixelFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].pixelFormat == format) - return i; - - return -1; -} - -static int indexOfVideoFormat(GstVideoFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].gstFormat == format) - return i; - - return -1; -} - -} - -QVideoSurfaceFormat QGstUtils::formatForCaps( - GstCaps *caps, GstVideoInfo *info, QAbstractVideoBuffer::HandleType handleType) -{ - GstVideoInfo vidInfo; - GstVideoInfo *infoPtr = info ? info : &vidInfo; - - if (gst_video_info_from_caps(infoPtr, caps)) { - int index = indexOfVideoFormat(infoPtr->finfo->format); - - if (index != -1) { - QVideoSurfaceFormat format( - QSize(infoPtr->width, infoPtr->height), - qt_videoFormatLookup[index].pixelFormat, - handleType); - - if (infoPtr->fps_d > 0) - format.setFrameRate(qreal(infoPtr->fps_n) / infoPtr->fps_d); - - if (infoPtr->par_d > 0) - format.setPixelAspectRatio(infoPtr->par_n, infoPtr->par_d); - - return format; - } - } - return QVideoSurfaceFormat(); -} - -GstCaps *QGstUtils::capsForFormats(const QList &formats) -{ - GstCaps *caps = gst_caps_new_empty(); - - for (QVideoFrame::PixelFormat format : formats) { - int index = indexOfVideoFormat(format); - - if (index != -1) { - gst_caps_append_structure(caps, gst_structure_new( - "video/x-raw", - "format" , G_TYPE_STRING, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat), - nullptr)); - } - } - - gst_caps_set_simple( - caps, - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, - "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, - nullptr); - - return caps; -} - -void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) -{ - // GStreamer uses nanoseconds, Qt uses microseconds - qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); - if (startTime >= 0) { - frame->setStartTime(startTime/G_GINT64_CONSTANT (1000)); - - qint64 duration = GST_BUFFER_DURATION(buffer); - if (duration >= 0) - frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); - } -} - -void QGstUtils::setMetaData(GstElement *element, const QMap &data) -{ - if (!GST_IS_TAG_SETTER(element)) - return; - - gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); - - for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) { - const QString tagName = QString::fromLatin1(it.key()); - const QVariant &tagValue = it.value(); - - switch (tagValue.typeId()) { - case QMetaType::QString: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toString().toUtf8().constData(), - nullptr); - break; - case QMetaType::Int: - case QMetaType::LongLong: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toInt(), - nullptr); - break; - case QMetaType::Double: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toDouble(), - nullptr); - break; - case QMetaType::QDateTime: { - QDateTime date = tagValue.toDateTime().toLocalTime(); - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - gst_date_time_new_local_time( - date.date().year(), date.date().month(), date.date().day(), - date.time().hour(), date.time().minute(), date.time().second()), - nullptr); - break; - } - default: - break; - } - } -} - -void QGstUtils::setMetaData(GstBin *bin, const QMap &data) -{ - GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER); - GValue item = G_VALUE_INIT; - while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) { - GstElement * const element = GST_ELEMENT(g_value_get_object(&item)); - setMetaData(element, data); - } - gst_iterator_free(elements); -} - - -GstCaps *QGstUtils::videoFilterCaps() -{ - const char *caps = - "video/x-raw(ANY);" - "image/jpeg;" - "video/x-h264"; - static GstStaticCaps staticCaps = GST_STATIC_CAPS(caps); - - return gst_caps_make_writable(gst_static_caps_get(&staticCaps)); -} - -QSize QGstUtils::structureResolution(const GstStructure *s) -{ - QSize size; - - int w, h; - if (s && gst_structure_get_int(s, "width", &w) && gst_structure_get_int(s, "height", &h)) { - size.rwidth() = w; - size.rheight() = h; - } - - return size; -} - -QVideoFrame::PixelFormat QGstUtils::structurePixelFormat(const GstStructure *structure) -{ - QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; - - if (!structure) - return pixelFormat; - - if (gst_structure_has_name(structure, "video/x-raw")) { - const gchar *s = gst_structure_get_string(structure, "format"); - if (s) { - GstVideoFormat format = gst_video_format_from_string(s); - int index = indexOfVideoFormat(format); - - if (index != -1) - pixelFormat = qt_videoFormatLookup[index].pixelFormat; - } - } - - return pixelFormat; -} - -QSize QGstUtils::structurePixelAspectRatio(const GstStructure *s) -{ - QSize ratio(1, 1); - - gint aspectNum = 0; - gint aspectDenum = 0; - if (s && gst_structure_get_fraction(s, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { - if (aspectDenum > 0) { - ratio.rwidth() = aspectNum; - ratio.rheight() = aspectDenum; - } - } - - return ratio; -} - -QPair QGstUtils::structureFrameRateRange(const GstStructure *s) -{ - QPair rate; - - if (!s) - return rate; - - int n, d; - if (gst_structure_get_fraction(s, "framerate", &n, &d)) { - rate.second = qreal(n) / d; - rate.first = rate.second; - } else if (gst_structure_get_fraction(s, "max-framerate", &n, &d)) { - rate.second = qreal(n) / d; - if (gst_structure_get_fraction(s, "min-framerate", &n, &d)) - rate.first = qreal(n) / d; - else - rate.first = qreal(1); - } - - return rate; -} - -typedef QMap FileExtensionMap; -Q_GLOBAL_STATIC(FileExtensionMap, fileExtensionMap) - -QString QGstUtils::fileExtensionForMimeType(const QString &mimeType) -{ - if (fileExtensionMap->isEmpty()) { - //extension for containers hard to guess from mimetype - fileExtensionMap->insert(QStringLiteral("video/x-matroska"), QLatin1String("mkv")); - fileExtensionMap->insert(QStringLiteral("video/quicktime"), QLatin1String("mov")); - fileExtensionMap->insert(QStringLiteral("video/x-msvideo"), QLatin1String("avi")); - fileExtensionMap->insert(QStringLiteral("video/msvideo"), QLatin1String("avi")); - fileExtensionMap->insert(QStringLiteral("audio/mpeg"), QLatin1String("mp3")); - fileExtensionMap->insert(QStringLiteral("application/x-shockwave-flash"), QLatin1String("swf")); - fileExtensionMap->insert(QStringLiteral("application/x-pn-realmedia"), QLatin1String("rm")); - } - - //for container names like avi instead of video/x-msvideo, use it as extension - if (!mimeType.contains(QLatin1Char('/'))) - return mimeType; - - QString format = mimeType.left(mimeType.indexOf(QLatin1Char(','))); - QString extension = fileExtensionMap->value(format); - - if (!extension.isEmpty() || format.isEmpty()) - return extension; - - QRegularExpression rx(QStringLiteral("[-/]([\\w]+)$")); - QRegularExpressionMatch match = rx.match(format); - - if (match.hasMatch()) - extension = match.captured(1); - - return extension; -} - -QVariant QGstUtils::fromGStreamerOrientation(const QVariant &value) -{ - // Note gstreamer tokens either describe the counter clockwise rotation of the - // image or the clockwise transform to apply to correct the image. The orientation - // value returned is the clockwise rotation of the image. - const QString token = value.toString(); - if (token == QStringLiteral("rotate-90")) - return 270; - if (token == QStringLiteral("rotate-180")) - return 180; - if (token == QStringLiteral("rotate-270")) - return 90; - return 0; -} - -QVariant QGstUtils::toGStreamerOrientation(const QVariant &value) -{ - switch (value.toInt()) { - case 90: - return QStringLiteral("rotate-270"); - case 180: - return QStringLiteral("rotate-180"); - case 270: - return QStringLiteral("rotate-90"); - default: - return QStringLiteral("rotate-0"); - } -} - -bool QGstUtils::useOpenGL() -{ - static bool result = qEnvironmentVariableIntValue("QT_GSTREAMER_USE_OPENGL_PLUGIN"); - return result; -} - -void qt_gst_object_ref_sink(gpointer object) -{ - gst_object_ref_sink(object); -} - -GstCaps *qt_gst_pad_get_current_caps(GstPad *pad) -{ - return gst_pad_get_current_caps(pad); -} - -GstCaps *qt_gst_pad_get_caps(GstPad *pad) -{ - return gst_pad_query_caps(pad, nullptr); -} - -GstStructure *qt_gst_structure_new_empty(const char *name) -{ - return gst_structure_new_empty(name); -} - -gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur) -{ - return gst_element_query_position(element, format, cur); -} - -gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur) -{ - return gst_element_query_duration(element, format, cur); -} - -GstCaps *qt_gst_caps_normalize(GstCaps *caps) -{ - // gst_caps_normalize() takes ownership of the argument in 1.0 - return gst_caps_normalize(caps); -} - -const gchar *qt_gst_element_get_factory_name(GstElement *element) -{ - const gchar *name = 0; - const GstElementFactory *factory = 0; - - if (element && (factory = gst_element_get_factory(element))) - name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); - - return name; -} - -gboolean qt_gst_caps_can_intersect(const GstCaps * caps1, const GstCaps * caps2) -{ - return gst_caps_can_intersect(caps1, caps2); -} - -GList *qt_gst_video_sinks() -{ - GList *list = nullptr; - - list = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, - GST_RANK_MARGINAL); - - return list; -} - -void qt_gst_util_double_to_fraction(gdouble src, gint *dest_n, gint *dest_d) -{ - gst_util_double_to_fraction(src, dest_n, dest_d); -} - -QDebug operator <<(QDebug debug, GstCaps *caps) -{ - if (caps) { - gchar *string = gst_caps_to_string(caps); - debug = debug << string; - g_free(string); - } - return debug; -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstutils_p.h b/src/gsttools/qgstutils_p.h deleted file mode 100644 index 6911f6427..000000000 --- a/src/gsttools/qgstutils_p.h +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTUTILS_P_H -#define QGSTUTILS_P_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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -# define QT_GSTREAMER_PLAYBIN_ELEMENT_NAME "playbin" -# define QT_GSTREAMER_CAMERABIN_ELEMENT_NAME "camerabin" -# define QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME "videoconvert" -# define QT_GSTREAMER_RAW_AUDIO_MIME "audio/x-raw" -# define QT_GSTREAMER_VIDEOOVERLAY_INTERFACE_NAME "GstVideoOverlay" - -QT_BEGIN_NAMESPACE - -class QSize; -class QVariant; -class QByteArray; -class QImage; -class QVideoSurfaceFormat; - -namespace QGstUtils { - struct Q_GSTTOOLS_EXPORT CameraInfo - { - QString name; - QString description; - int orientation; - QCamera::Position position; - QByteArray driver; - }; - - Q_GSTTOOLS_EXPORT QMap gstTagListToMap(const GstTagList *list); - - Q_GSTTOOLS_EXPORT QSize capsResolution(const GstCaps *caps); - Q_GSTTOOLS_EXPORT QSize capsCorrectedResolution(const GstCaps *caps); - Q_GSTTOOLS_EXPORT QAudioFormat audioFormatForCaps(const GstCaps *caps); - Q_GSTTOOLS_EXPORT QAudioFormat audioFormatForSample(GstSample *sample); - Q_GSTTOOLS_EXPORT GstCaps *capsForAudioFormat(const QAudioFormat &format); - Q_GSTTOOLS_EXPORT void initializeGst(); - Q_GSTTOOLS_EXPORT QMultimedia::SupportEstimate hasSupport(const QString &mimeType, - const QStringList &codecs, - const QSet &supportedMimeTypeSet); - - Q_GSTTOOLS_EXPORT QList enumerateCameras(); - Q_GSTTOOLS_EXPORT QList cameraDevices(); - Q_GSTTOOLS_EXPORT QString cameraDescription(const QString &device); - Q_GSTTOOLS_EXPORT QCamera::Position cameraPosition(const QString &device); - Q_GSTTOOLS_EXPORT int cameraOrientation(const QString &device); - Q_GSTTOOLS_EXPORT QByteArray cameraDriver(const QString &device); - - Q_GSTTOOLS_EXPORT const QSet &audioSources(); - Q_GSTTOOLS_EXPORT const QSet &audioSinks(); - - Q_GSTTOOLS_EXPORT QSet supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)); - - Q_GSTTOOLS_EXPORT QImage bufferToImage(GstBuffer *buffer, const GstVideoInfo &info); - Q_GSTTOOLS_EXPORT QVideoSurfaceFormat formatForCaps( - GstCaps *caps, - GstVideoInfo *info = 0, - QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); - - Q_GSTTOOLS_EXPORT GstCaps *capsForFormats(const QList &formats); - void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); - - Q_GSTTOOLS_EXPORT void setMetaData(GstElement *element, const QMap &data); - Q_GSTTOOLS_EXPORT void setMetaData(GstBin *bin, const QMap &data); - - Q_GSTTOOLS_EXPORT GstCaps *videoFilterCaps(); - - Q_GSTTOOLS_EXPORT QSize structureResolution(const GstStructure *s); - Q_GSTTOOLS_EXPORT QVideoFrame::PixelFormat structurePixelFormat(const GstStructure *s); - Q_GSTTOOLS_EXPORT QSize structurePixelAspectRatio(const GstStructure *s); - Q_GSTTOOLS_EXPORT QPair structureFrameRateRange(const GstStructure *s); - - Q_GSTTOOLS_EXPORT QString fileExtensionForMimeType(const QString &mimeType); - - Q_GSTTOOLS_EXPORT QVariant fromGStreamerOrientation(const QVariant &value); - Q_GSTTOOLS_EXPORT QVariant toGStreamerOrientation(const QVariant &value); - - Q_GSTTOOLS_EXPORT bool useOpenGL(); -} - -Q_GSTTOOLS_EXPORT void qt_gst_object_ref_sink(gpointer object); -Q_GSTTOOLS_EXPORT GstCaps *qt_gst_pad_get_current_caps(GstPad *pad); -Q_GSTTOOLS_EXPORT GstCaps *qt_gst_pad_get_caps(GstPad *pad); -Q_GSTTOOLS_EXPORT GstStructure *qt_gst_structure_new_empty(const char *name); -Q_GSTTOOLS_EXPORT gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur); -Q_GSTTOOLS_EXPORT gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur); -Q_GSTTOOLS_EXPORT GstCaps *qt_gst_caps_normalize(GstCaps *caps); -Q_GSTTOOLS_EXPORT const gchar *qt_gst_element_get_factory_name(GstElement *element); -Q_GSTTOOLS_EXPORT gboolean qt_gst_caps_can_intersect(const GstCaps * caps1, const GstCaps * caps2); -Q_GSTTOOLS_EXPORT GList *qt_gst_video_sinks(); -Q_GSTTOOLS_EXPORT void qt_gst_util_double_to_fraction(gdouble src, gint *dest_n, gint *dest_d); - -Q_GSTTOOLS_EXPORT QDebug operator <<(QDebug debug, GstCaps *caps); - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstvideobuffer.cpp b/src/gsttools/qgstvideobuffer.cpp deleted file mode 100644 index 58738ffa0..000000000 --- a/src/gsttools/qgstvideobuffer.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** 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 "qgstvideobuffer_p.h" - -QT_BEGIN_NAMESPACE - -QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info) - : QAbstractVideoBuffer(NoHandle) - , m_videoInfo(info) - , m_buffer(buffer) -{ - gst_buffer_ref(m_buffer); -} - -QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, - QGstVideoBuffer::HandleType handleType, - const QVariant &handle) - : QAbstractVideoBuffer(handleType) - , m_videoInfo(info) - , m_buffer(buffer) - , m_handle(handle) -{ - gst_buffer_ref(m_buffer); -} - -QGstVideoBuffer::~QGstVideoBuffer() -{ - unmap(); - - gst_buffer_unref(m_buffer); -} - - -QAbstractVideoBuffer::MapMode QGstVideoBuffer::mapMode() const -{ - return m_mode; -} - -QAbstractVideoBuffer::MapData QGstVideoBuffer::map(MapMode mode) -{ - const GstMapFlags flags = GstMapFlags(((mode & ReadOnly) ? GST_MAP_READ : 0) - | ((mode & WriteOnly) ? GST_MAP_WRITE : 0)); - - MapData mapData; - if (mode == NotMapped || m_mode != NotMapped) - return mapData; - - if (m_videoInfo.finfo->n_planes == 0) { // Encoded - if (gst_buffer_map(m_buffer, &m_frame.map[0], flags)) { - mapData.nBytes = m_frame.map[0].size; - mapData.nPlanes = 1; - mapData.bytesPerLine[0] = -1; - mapData.data[0] = static_cast(m_frame.map[0].data); - - m_mode = mode; - } - } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, flags)) { - mapData.nBytes = m_frame.info.size; - mapData.nPlanes = m_frame.info.finfo->n_planes; - - for (guint i = 0; i < m_frame.info.finfo->n_planes; ++i) { - mapData.bytesPerLine[i] = m_frame.info.stride[i]; - mapData.data[i] = static_cast(m_frame.data[i]); - } - - m_mode = mode; - } - return mapData; -} - -void QGstVideoBuffer::unmap() -{ - if (m_mode != NotMapped) { - if (m_videoInfo.finfo->n_planes == 0) - gst_buffer_unmap(m_buffer, &m_frame.map[0]); - else - gst_video_frame_unmap(&m_frame); - } - m_mode = NotMapped; -} - -QT_END_NAMESPACE diff --git a/src/gsttools/qgstvideobuffer_p.h b/src/gsttools/qgstvideobuffer_p.h deleted file mode 100644 index 4dc45a370..000000000 --- a/src/gsttools/qgstvideobuffer_p.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QGSTVIDEOBUFFER_P_H -#define QGSTVIDEOBUFFER_P_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 -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_GSTTOOLS_EXPORT QGstVideoBuffer : public QAbstractVideoBuffer -{ -public: - QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info); - QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, - HandleType handleType, const QVariant &handle); - - ~QGstVideoBuffer(); - - GstBuffer *buffer() const { return m_buffer; } - MapMode mapMode() const override; - - MapData map(MapMode mode) override; - void unmap() override; - - QVariant handle() const override { return m_handle; } -private: - GstVideoInfo m_videoInfo; - GstVideoFrame m_frame; - GstBuffer *m_buffer = nullptr; - MapMode m_mode = NotMapped; - QVariant m_handle; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstvideorendererplugin.cpp b/src/gsttools/qgstvideorendererplugin.cpp deleted file mode 100644 index 22028ac0e..000000000 --- a/src/gsttools/qgstvideorendererplugin.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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 "qgstvideorendererplugin_p.h" - -QT_BEGIN_NAMESPACE - -QGstVideoRendererPlugin::QGstVideoRendererPlugin(QObject *parent) : - QObject(parent) -{ -} - -QT_END_NAMESPACE - -#include "moc_qgstvideorendererplugin_p.cpp" diff --git a/src/gsttools/qgstvideorendererplugin_p.h b/src/gsttools/qgstvideorendererplugin_p.h deleted file mode 100644 index 3635fea35..000000000 --- a/src/gsttools/qgstvideorendererplugin_p.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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 QGSTVIDEORENDERERPLUGIN_P_H -#define QGSTVIDEORENDERERPLUGIN_P_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 -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class QAbstractVideoSurface; - -class Q_GSTTOOLS_EXPORT QGstVideoRenderer -{ -public: - virtual ~QGstVideoRenderer() {} - - virtual GstCaps *getCaps(QAbstractVideoSurface *surface) = 0; - virtual bool start(QAbstractVideoSurface *surface, GstCaps *caps) = 0; - virtual void stop(QAbstractVideoSurface *surface) = 0; // surface may be null if unexpectedly deleted. - virtual bool proposeAllocation(GstQuery *query) = 0; // may be called from a thread. - - virtual bool present(QAbstractVideoSurface *surface, GstBuffer *buffer) = 0; - virtual void flush(QAbstractVideoSurface *surface) = 0; // surface may be null if unexpectedly deleted. -}; - -/* - Abstract interface for video buffers allocation. -*/ -class Q_GSTTOOLS_EXPORT QGstVideoRendererInterface -{ -public: - virtual ~QGstVideoRendererInterface() {} - - virtual QGstVideoRenderer *createRenderer() = 0; -}; - -#define QGstVideoRendererInterface_iid "org.qt-project.qt.gstvideorenderer/5.4" -Q_DECLARE_INTERFACE(QGstVideoRendererInterface, QGstVideoRendererInterface_iid) - -class Q_GSTTOOLS_EXPORT QGstVideoRendererPlugin : public QObject, public QGstVideoRendererInterface -{ - Q_OBJECT - Q_INTERFACES(QGstVideoRendererInterface) -public: - explicit QGstVideoRendererPlugin(QObject *parent = 0); - virtual ~QGstVideoRendererPlugin() {} - - QGstVideoRenderer *createRenderer() override = 0; - -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/gsttools/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp deleted file mode 100644 index 0f930cf02..000000000 --- a/src/gsttools/qgstvideorenderersink.cpp +++ /dev/null @@ -1,803 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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 -#include -#include -#include -#include -#include -#include - -#include -#include "qgstvideobuffer_p.h" - -#include "qgstvideorenderersink_p.h" - -#include - -#include "qgstutils_p.h" - -#if QT_CONFIG(gstreamer_gl) -#include -#include -#include -#include - -#include - -#if GST_GL_HAVE_WINDOW_X11 -# include -#endif -#if GST_GL_HAVE_PLATFORM_EGL -# include -#endif -#if GST_GL_HAVE_WINDOW_WAYLAND -# include -#endif -#endif // #if QT_CONFIG(gstreamer_gl) - -//#define DEBUG_VIDEO_SURFACE_SINK - -QT_BEGIN_NAMESPACE - -QGstDefaultVideoRenderer::QGstDefaultVideoRenderer() -{ -} - -QGstDefaultVideoRenderer::~QGstDefaultVideoRenderer() -{ -} - -GstCaps *QGstDefaultVideoRenderer::getCaps(QAbstractVideoSurface *surface) -{ -#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_handleType); - - return m_format.isValid() && surface->start(m_format); -} - -void QGstDefaultVideoRenderer::stop(QAbstractVideoSurface *surface) -{ - m_flushed = true; - if (surface) - surface->stop(); -} - -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); - - auto meta = gst_buffer_get_video_crop_meta (buffer); - if (meta) { - QRect vp(meta->x, meta->y, meta->width, meta->height); - if (m_format.viewport() != vp) { -#ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << Q_FUNC_INFO << " Update viewport on Metadata: [" << meta->height << "x" << meta->width << " | " << meta->x << "x" << meta->y << "]"; -#endif - //Update viewport if data is not the same - m_format.setViewport(vp); - surface->start(m_format); - } - } - - QVideoFrame frame( - videoBuffer, - m_format.frameSize(), - m_format.pixelFormat()); - QGstUtils::setFrameTimeStamps(&frame, buffer); - - return surface->present(frame); -} - -void QGstDefaultVideoRenderer::flush(QAbstractVideoSurface *surface) -{ - if (surface && !m_flushed) - surface->present(QVideoFrame()); - m_flushed = true; -} - -bool QGstDefaultVideoRenderer::proposeAllocation(GstQuery *) -{ - return true; -} - -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, rendererLoader, - (QGstVideoRendererInterface_iid, QLatin1String("video/gstvideorenderer"), Qt::CaseInsensitive)) - -QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface) - : m_surface(surface) -{ - int i = 0; - while (QObject *instance = rendererLoader->instance(i)) { - auto plugin = qobject_cast(instance); - if (QGstVideoRenderer *renderer = plugin ? plugin->createRenderer() : nullptr) - m_renderers.append(renderer); - ++i; - } - - m_renderers.append(new QGstDefaultVideoRenderer); - updateSupportedFormats(); - connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats())); -} - -QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate() -{ - qDeleteAll(m_renderers); - - if (m_surfaceCaps) - 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() -{ - QMutexLocker locker(&m_mutex); - - gst_caps_ref(m_surfaceCaps); - - return m_surfaceCaps; -} - -bool QVideoSurfaceGstDelegate::start(GstCaps *caps) -{ - QMutexLocker locker(&m_mutex); - - if (m_activeRenderer) { - m_flush = true; - m_stop = true; - } - - if (m_startCaps) - gst_caps_unref(m_startCaps); - m_startCaps = caps; - gst_caps_ref(m_startCaps); - - /* - Waiting for start() to be invoked in the main thread may block - if gstreamer blocks the main thread until this call is finished. - This situation is rare and usually caused by setState(Null) - while pipeline is being prerolled. - - The proper solution to this involves controlling gstreamer pipeline from - other thread than video surface. - - Currently start() fails if wait() timed out. - */ - if (!waitForAsyncEvent(&locker, &m_setupCondition, 1000) && m_startCaps) { - qWarning() << "Failed to start video surface due to main thread blocked."; - gst_caps_unref(m_startCaps); - m_startCaps = 0; - } - - return m_activeRenderer != 0; -} - -void QVideoSurfaceGstDelegate::stop() -{ - QMutexLocker locker(&m_mutex); - - if (!m_activeRenderer) - return; - - m_flush = true; - m_stop = true; - - if (m_startCaps) { - gst_caps_unref(m_startCaps); - m_startCaps = 0; - } - - waitForAsyncEvent(&locker, &m_setupCondition, 500); -} - -void QVideoSurfaceGstDelegate::unlock() -{ - QMutexLocker locker(&m_mutex); - - m_setupCondition.wakeAll(); - m_renderCondition.wakeAll(); -} - -bool QVideoSurfaceGstDelegate::proposeAllocation(GstQuery *query) -{ - QMutexLocker locker(&m_mutex); - - if (QGstVideoRenderer *pool = m_activeRenderer) { - locker.unlock(); - - return pool->proposeAllocation(query); - } - - return false; -} - -void QVideoSurfaceGstDelegate::flush() -{ - QMutexLocker locker(&m_mutex); - - m_flush = true; - m_renderBuffer = 0; - m_renderCondition.wakeAll(); - - notify(); -} - -GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) -{ - QMutexLocker locker(&m_mutex); - - m_renderReturn = GST_FLOW_OK; - m_renderBuffer = buffer; - - waitForAsyncEvent(&locker, &m_renderCondition, 300); - - m_renderBuffer = 0; - - return m_renderReturn; -} - -#if QT_CONFIG(gstreamer_gl) -static GstGLContext *gstGLDisplayContext(QAbstractVideoSurface *surface) -{ - auto glContext = qobject_cast(surface->property("GLContext").value()); - // 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_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 - - 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 = nullptr; - 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 = nullptr; - gst_query_parse_context(query, &context); - context = context ? gst_context_copy(context) : gst_context_new(type, FALSE); - GstStructure *structure = gst_context_writable_structure(context); - gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, m_gstGLDisplayContext, nullptr); - 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) { - QMutexLocker locker(&m_mutex); - - if (m_notified) { - while (handleEvent(&locker)) {} - m_notified = false; - } - return true; - } - - return QObject::event(event); -} - -bool QVideoSurfaceGstDelegate::handleEvent(QMutexLocker *locker) -{ - if (m_flush) { - m_flush = false; - if (m_activeRenderer) { - locker->unlock(); - - m_activeRenderer->flush(m_surface); - } - } else if (m_stop) { - m_stop = false; - - if (QGstVideoRenderer * const activePool = m_activeRenderer) { - m_activeRenderer = 0; - locker->unlock(); - - activePool->stop(m_surface); - - locker->relock(); - } - } else if (m_startCaps) { - Q_ASSERT(!m_activeRenderer); - - GstCaps * const startCaps = m_startCaps; - m_startCaps = 0; - - if (m_renderer && m_surface) { - locker->unlock(); - - const bool started = m_renderer->start(m_surface, startCaps); - - locker->relock(); - - m_activeRenderer = started - ? m_renderer - : 0; - } else if (QGstVideoRenderer * const activePool = m_activeRenderer) { - m_activeRenderer = 0; - locker->unlock(); - - activePool->stop(m_surface); - - locker->relock(); - } - - gst_caps_unref(startCaps); - } else if (m_renderBuffer) { - GstBuffer *buffer = m_renderBuffer; - m_renderBuffer = 0; - m_renderReturn = GST_FLOW_ERROR; - - if (m_activeRenderer && m_surface) { - gst_buffer_ref(buffer); - - locker->unlock(); - - const bool rendered = m_activeRenderer->present(m_surface, buffer); - - gst_buffer_unref(buffer); - - locker->relock(); - - if (rendered) - m_renderReturn = GST_FLOW_OK; - } - - m_renderCondition.wakeAll(); - } else { - m_setupCondition.wakeAll(); - - return false; - } - return true; -} - -void QVideoSurfaceGstDelegate::notify() -{ - if (!m_notified) { - m_notified = true; - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); - } -} - -bool QVideoSurfaceGstDelegate::waitForAsyncEvent( - QMutexLocker *locker, QWaitCondition *condition, unsigned long time) -{ - if (QThread::currentThread() == thread()) { - while (handleEvent(locker)) {} - m_notified = false; - - return true; - } - - notify(); - - return condition->wait(&m_mutex, time); -} - -void QVideoSurfaceGstDelegate::updateSupportedFormats() -{ - if (m_surfaceCaps) { - gst_caps_unref(m_surfaceCaps); - m_surfaceCaps = 0; - } - - for (QGstVideoRenderer *pool : qAsConst(m_renderers)) { - if (GstCaps *caps = pool->getCaps(m_surface)) { - if (gst_caps_is_empty(caps)) { - gst_caps_unref(caps); - continue; - } - - if (m_surfaceCaps) - gst_caps_unref(m_surfaceCaps); - - m_renderer = pool; - m_surfaceCaps = caps; - break; - } - } -} - -static GstVideoSinkClass *sink_parent_class; -static QAbstractVideoSurface *current_surface; - -#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast(s)) - -QGstVideoRendererSink *QGstVideoRendererSink::createSink(QAbstractVideoSurface *surface) -{ - setSurface(surface); - QGstVideoRendererSink *sink = reinterpret_cast( - g_object_new(QGstVideoRendererSink::get_type(), 0)); - - g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); - - return sink; -} - -void QGstVideoRendererSink::setSurface(QAbstractVideoSurface *surface) -{ - current_surface = surface; - get_type(); -} - -GType QGstVideoRendererSink::get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = - { - sizeof(QGstVideoRendererSinkClass), // class_size - base_init, // base_init - nullptr, // base_finalize - class_init, // class_init - nullptr, // class_finalize - nullptr, // class_data - sizeof(QGstVideoRendererSink), // instance_size - 0, // n_preallocs - instance_init, // instance_init - 0 // value_table - }; - - type = g_type_register_static( - GST_TYPE_VIDEO_SINK, "QGstVideoRendererSink", &info, GTypeFlags(0)); - - // Register the sink type to be used in custom piplines. - // When surface is ready the sink can be used. - gst_element_register(nullptr, "qtvideosink", GST_RANK_PRIMARY, type); - } - - return type; -} - -void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data) -{ - Q_UNUSED(class_data); - - sink_parent_class = reinterpret_cast(g_type_class_peek_parent(g_class)); - - GstVideoSinkClass *video_sink_class = reinterpret_cast(g_class); - video_sink_class->show_frame = QGstVideoRendererSink::show_frame; - - GstBaseSinkClass *base_sink_class = reinterpret_cast(g_class); - base_sink_class->get_caps = QGstVideoRendererSink::get_caps; - base_sink_class->set_caps = QGstVideoRendererSink::set_caps; - 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(g_class); - element_class->change_state = QGstVideoRendererSink::change_state; - gst_element_class_set_metadata(element_class, - "Qt built-in video renderer sink", - "Sink/Video", - "Qt default built-in video renderer sink", - "The Qt Company"); - - GObjectClass *object_class = reinterpret_cast(g_class); - object_class->finalize = QGstVideoRendererSink::finalize; -} - -void QGstVideoRendererSink::base_init(gpointer g_class) -{ - static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( - "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( - "video/x-raw, " - "framerate = (fraction) [ 0, MAX ], " - "width = (int) [ 1, MAX ], " - "height = (int) [ 1, MAX ]")); - - gst_element_class_add_pad_template( - GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); -} - -struct NullSurface : QAbstractVideoSurface -{ - NullSurface(QObject *parent = nullptr) : QAbstractVideoSurface(parent) { } - - QList supportedPixelFormats(QAbstractVideoBuffer::HandleType) const override - { - return QList() << QVideoFrame::Format_RGB32; - } - - bool present(const QVideoFrame &) override - { - return true; - } -}; - -void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class) -{ - Q_UNUSED(g_class); - VO_SINK(instance); - - if (!current_surface) { - qWarning() << "Using qtvideosink element without video surface"; - static NullSurface nullSurface; - current_surface = &nullSurface; - } - - sink->delegate = new QVideoSurfaceGstDelegate(current_surface); - sink->delegate->moveToThread(current_surface->thread()); - current_surface = nullptr; -} - -void QGstVideoRendererSink::finalize(GObject *object) -{ - VO_SINK(object); - - delete sink->delegate; - - // Chain up - G_OBJECT_CLASS(sink_parent_class)->finalize(object); -} - -void QGstVideoRendererSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) -{ - Q_UNUSED(o); - Q_UNUSED(p); - QGstVideoRendererSink *sink = reinterpret_cast(d); - - gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, nullptr); - - if (!showPrerollFrame) { - GstState state = GST_STATE_VOID_PENDING; - GstClockTime timeout = 10000000; // 10 ms - gst_element_get_state(GST_ELEMENT(sink), &state, nullptr, timeout); - // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means - // the QMediaPlayer was stopped from the paused state. - // We need to flush the current frame. - if (state == GST_STATE_PAUSED) - sink->delegate->flush(); - } -} - -GstStateChangeReturn QGstVideoRendererSink::change_state( - GstElement *element, GstStateChange transition) -{ - QGstVideoRendererSink *sink = reinterpret_cast(element); - - gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, nullptr); - - // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to - // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. - // We need to flush the current frame. - if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) - sink->delegate->flush(); - - return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); -} - -GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter) -{ - VO_SINK(base); - - GstCaps *caps = sink->delegate->caps(); - GstCaps *unfiltered = caps; - if (filter) { - caps = gst_caps_intersect(unfiltered, filter); - gst_caps_unref(unfiltered); - } - - return caps; -} - -gboolean QGstVideoRendererSink::set_caps(GstBaseSink *base, GstCaps *caps) -{ - VO_SINK(base); - -#ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << "set_caps:"; - qDebug() << caps; -#endif - - if (!caps) { - sink->delegate->stop(); - - return TRUE; - } else if (sink->delegate->start(caps)) { - return TRUE; - } else { - return FALSE; - } -} - -gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *base, GstQuery *query) -{ - VO_SINK(base); - return sink->delegate->proposeAllocation(query); -} - -gboolean QGstVideoRendererSink::stop(GstBaseSink *base) -{ - VO_SINK(base); - sink->delegate->stop(); - return TRUE; -} - -gboolean QGstVideoRendererSink::unlock(GstBaseSink *base) -{ - VO_SINK(base); - sink->delegate->unlock(); - return TRUE; -} - -GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *base, GstBuffer *buffer) -{ - VO_SINK(base); - 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 deleted file mode 100644 index 81bcb5c02..000000000 --- a/src/gsttools/qgstvideorenderersink_p.h +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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 QGSTVIDEORENDERERSINK_P_H -#define QGSTVIDEORENDERERSINK_P_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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "qgstvideorendererplugin_p.h" - -#include "qgstvideorendererplugin_p.h" - -#if QT_CONFIG(gstreamer_gl) -#ifndef GST_USE_UNSTABLE_API -#define GST_USE_UNSTABLE_API -#endif -#include -#endif - -QT_BEGIN_NAMESPACE -class QAbstractVideoSurface; - -class QGstDefaultVideoRenderer : public QGstVideoRenderer -{ -public: - QGstDefaultVideoRenderer(); - ~QGstDefaultVideoRenderer(); - - GstCaps *getCaps(QAbstractVideoSurface *surface) override; - bool start(QAbstractVideoSurface *surface, GstCaps *caps) override; - void stop(QAbstractVideoSurface *surface) override; - - bool proposeAllocation(GstQuery *query) override; - - bool present(QAbstractVideoSurface *surface, GstBuffer *buffer) override; - void flush(QAbstractVideoSurface *surface) override; - -private: - QVideoSurfaceFormat m_format; - GstVideoInfo m_videoInfo; - bool m_flushed = true; - QAbstractVideoBuffer::HandleType m_handleType = QAbstractVideoBuffer::NoHandle; -}; - -class QVideoSurfaceGstDelegate : public QObject -{ - Q_OBJECT -public: - QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface); - ~QVideoSurfaceGstDelegate(); - - GstCaps *caps(); - - bool start(GstCaps *caps); - void stop(); - void unlock(); - bool proposeAllocation(GstQuery *query); - - void flush(); - - GstFlowReturn render(GstBuffer *buffer); - - bool event(QEvent *event) override; - bool query(GstQuery *query); - -private slots: - bool handleEvent(QMutexLocker *locker); - void updateSupportedFormats(); - -private: - void notify(); - bool waitForAsyncEvent(QMutexLocker *locker, QWaitCondition *condition, unsigned long time); - - QPointer m_surface; - - QMutex m_mutex; - QWaitCondition m_setupCondition; - QWaitCondition m_renderCondition; - GstFlowReturn m_renderReturn = GST_FLOW_OK; - QList m_renderers; - QGstVideoRenderer *m_renderer = nullptr; - QGstVideoRenderer *m_activeRenderer = nullptr; - - 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; - bool m_flush = false; -}; - -class Q_GSTTOOLS_EXPORT QGstVideoRendererSink -{ -public: - GstVideoSink parent; - - static QGstVideoRendererSink *createSink(QAbstractVideoSurface *surface); - static void setSurface(QAbstractVideoSurface *surface); - -private: - static GType get_type(); - static void class_init(gpointer g_class, gpointer class_data); - static void base_init(gpointer g_class); - static void instance_init(GTypeInstance *instance, gpointer g_class); - - static void finalize(GObject *object); - - static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); - - static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); - - static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter); - static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); - - static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query); - - static gboolean stop(GstBaseSink *sink); - - static gboolean unlock(GstBaseSink *sink); - - static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer); - static gboolean query(GstBaseSink *element, GstQuery *query); - -private: - QVideoSurfaceGstDelegate *delegate = nullptr; -}; - - -class QGstVideoRendererSinkClass -{ -public: - GstVideoSinkClass parent_class; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/CMakeLists.txt b/src/multimedia/CMakeLists.txt index 114ac3b6e..a912453b1 100644 --- a/src/multimedia/CMakeLists.txt +++ b/src/multimedia/CMakeLists.txt @@ -243,3 +243,62 @@ qt_extend_target(Multimedia CONDITION QT_FEATURE_alsa qt_add_docs(Multimedia doc/qtmultimedia.qdocconf ) + +qt_extend_target(Multimedia CONDITION QT_FEATURE_gstreamer + SOURCES + gstreamer/qgstcodecsinfo.cpp qgstcodecsinfo_p.h + gstreamer/qgstreameraudioinputselector.cpp gstreamer/qgstreameraudioinputselector_p.h + gstreamer/qgstreameraudioprobecontrol.cpp gstreamer/qgstreameraudioprobecontrol_p.h + gstreamer/qgstreamerbufferprobe.cpp gstreamer/qgstreamerbufferprobe_p.h + gstreamer/qgstreamerbushelper.cpp gstreamer/qgstreamerbushelper_p.h + gstreamer/qgstreamermessage.cpp gstreamer/qgstreamermessage_p.h + gstreamer/qgstreamerplayercontrol.cpp gstreamer/qgstreamerplayercontrol_p.h + gstreamer/qgstreamerplayersession.cpp gstreamer/qgstreamerplayersession_p.h + gstreamer/qgstreamervideoinputdevicecontrol.cpp gstreamer/qgstreamervideoinputdevicecontrol_p.h + gstreamer/qgstreamervideooverlay.cpp gstreamer/qgstreamervideooverlay_p.h + gstreamer/qgstreamervideoprobecontrol.cpp gstreamer/qgstreamervideoprobecontrol_p.h + gstreamer/qgstreamervideorenderer.cpp gstreamer/qgstreamervideorenderer_p.h + gstreamer/qgstreamervideorendererinterface.cpp gstreamer/qgstreamervideorendererinterface_p.h + gstreamer/qgstreamervideowindow.cpp gstreamer/qgstreamervideowindow_p.h + gstreamer/qgstutils.cpp gstreamer/qgstutils_p.h + gstreamer/qgstvideobuffer.cpp gstreamer/qgstvideobuffer_p.h + gstreamer/qgstvideorendererplugin.cpp gstreamer/qgstvideorendererplugin_p.h + gstreamer/qgstvideorenderersink.cpp gstreamer/qgstvideorenderersink_p.h + DEFINES + GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26 + PUBLIC_LIBRARIES + gstreamer +) + +qt_extend_target(Multimedia CONDITION QT_FEATURE_gstreamer_gl + PUBLIC_LIBRARIES + gstreamer_gl +) + +qt_extend_target(Multimedia CONDITION QT_FEATURE_gstreamer_app + SOURCES + gstreamer/qgstappsrc.cpp gstreamer/qgstappsrc_p.h # special case + PUBLIC_LIBRARIES + gstreamer_app +) + +qt_extend_target(Multimedia CONDITION QT_FEATURE_gstreamer AND ANDROID + LIBRARIES + # Remove: L$ENV{GSTREAMER_ROOT_ANDROID}/armv7/lib + # Remove: Wl,--no-whole-archive + # Remove: Wl,--whole-archive + WrapIconv::WrapIconv + ffi + glib-2.0 + gmodule-2.0 + gobject-2.0 + gstapp-1.0 + gstaudio-1.0 + gstbase-1.0 + gstpbutils-1.0 + gstreamer-1.0 + gsttag-1.0 + gstvideo-1.0 + intl + orc-0.4 +) diff --git a/src/multimedia/gstreamer/gstreamer.pri b/src/multimedia/gstreamer/gstreamer.pri new file mode 100644 index 000000000..5a9ec0d67 --- /dev/null +++ b/src/multimedia/gstreamer/gstreamer.pri @@ -0,0 +1,64 @@ +DEFINES += GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26 + +qtConfig(alsa): \ + QMAKE_USE += alsa + +QMAKE_USE += gstreamer + +HEADERS += \ + gstreamer/qgstreamerbushelper_p.h \ + gstreamer/qgstreamermessage_p.h \ + gstreamer/qgstutils_p.h \ + gstreamer/qgstvideobuffer_p.h \ + gstreamer/qgstreamerbufferprobe_p.h \ + gstreamer/qgstreamervideorendererinterface_p.h \ + gstreamer/qgstreameraudioinputselector_p.h \ + gstreamer/qgstreamervideorenderer_p.h \ + gstreamer/qgstreamervideoinputdevicecontrol_p.h \ + gstreamer/qgstcodecsinfo_p.h \ + gstreamer/qgstreamervideoprobecontrol_p.h \ + gstreamer/qgstreameraudioprobecontrol_p.h \ + gstreamer/qgstreamervideowindow_p.h \ + gstreamer/qgstreamervideooverlay_p.h \ + gstreamer/qgstreamerplayersession_p.h \ + gstreamer/qgstreamerplayercontrol_p.h \ + gstreamer/qgstvideorendererplugin_p.h \ + gstreamer/qgstvideorenderersink_p.h + +SOURCES += \ + gstreamer/qgstreamerbushelper.cpp \ + gstreamer/qgstreamermessage.cpp \ + gstreamer/qgstutils.cpp \ + gstreamer/qgstvideobuffer.cpp \ + gstreamer/qgstreamerbufferprobe.cpp \ + gstreamer/qgstreamervideorendererinterface.cpp \ + gstreamer/qgstreameraudioinputselector.cpp \ + gstreamer/qgstreamervideorenderer.cpp \ + gstreamer/qgstreamervideoinputdevicecontrol.cpp \ + gstreamer/qgstcodecsinfo.cpp \ + gstreamer/qgstreamervideoprobecontrol.cpp \ + gstreamer/qgstreameraudioprobecontrol.cpp \ + gstreamer/qgstreamervideowindow.cpp \ + gstreamer/qgstreamervideooverlay.cpp \ + gstreamer/qgstreamerplayersession.cpp \ + gstreamer/qgstreamerplayercontrol.cpp \ + gstreamer/qgstvideorendererplugin.cpp \ + gstreamer/qgstvideorenderersink.cpp + +qtConfig(gstreamer_gl): QMAKE_USE += gstreamer_gl + +qtConfig(gstreamer_app) { + QMAKE_USE += gstreamer_app + PRIVATE_HEADERS += gstreamer/qgstappsrc_p.h + SOURCES += gstreamer/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 +} + diff --git a/src/multimedia/gstreamer/qgstappsrc.cpp b/src/multimedia/gstreamer/qgstappsrc.cpp new file mode 100644 index 000000000..e8ec63bb9 --- /dev/null +++ b/src/multimedia/gstreamer/qgstappsrc.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** 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 + +#include "qgstappsrc_p.h" + +QGstAppSrc::QGstAppSrc(QObject *parent) + : QObject(parent) +{ + m_callbacks.need_data = &QGstAppSrc::on_need_data; + m_callbacks.enough_data = &QGstAppSrc::on_enough_data; + m_callbacks.seek_data = &QGstAppSrc::on_seek_data; +} + +QGstAppSrc::~QGstAppSrc() +{ + if (m_appSrc) + gst_object_unref(G_OBJECT(m_appSrc)); +} + +bool QGstAppSrc::setup(GstElement* appsrc) +{ + if (m_appSrc) { + gst_object_unref(G_OBJECT(m_appSrc)); + m_appSrc = 0; + } + + if (!appsrc || !m_stream) + return false; + + m_appSrc = GST_APP_SRC(appsrc); + gst_object_ref(G_OBJECT(m_appSrc)); + gst_app_src_set_callbacks(m_appSrc, (GstAppSrcCallbacks*)&m_callbacks, this, (GDestroyNotify)&QGstAppSrc::destroy_notify); + + g_object_get(G_OBJECT(m_appSrc), "max-bytes", &m_maxBytes, nullptr); + + if (m_sequential) + m_streamType = GST_APP_STREAM_TYPE_STREAM; + else + m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS; + gst_app_src_set_stream_type(m_appSrc, m_streamType); + gst_app_src_set_size(m_appSrc, (m_sequential) ? -1 : m_stream->size()); + + return true; +} + +void QGstAppSrc::setStream(QIODevice *stream) +{ + if (m_stream) { + disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); + disconnect(m_stream, SIGNAL(destroyed()), this, SLOT(streamDestroyed())); + m_stream = 0; + } + + if (m_appSrc) { + gst_object_unref(G_OBJECT(m_appSrc)); + m_appSrc = 0; + } + + m_dataRequestSize = ~0; + m_dataRequested = false; + m_enoughData = false; + m_forceData = false; + m_sequential = false; + m_maxBytes = 0; + + if (stream) { + m_stream = stream; + connect(m_stream, SIGNAL(destroyed()), SLOT(streamDestroyed())); + connect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); + m_sequential = m_stream->isSequential(); + } +} + +QIODevice *QGstAppSrc::stream() const +{ + return m_stream; +} + +GstAppSrc *QGstAppSrc::element() +{ + return m_appSrc; +} + +void QGstAppSrc::onDataReady() +{ + if (!m_enoughData) { + m_dataRequested = true; + pushDataToAppSrc(); + } +} + +void QGstAppSrc::streamDestroyed() +{ + if (sender() == m_stream) { + m_stream = 0; + sendEOS(); + } +} + +void QGstAppSrc::pushDataToAppSrc() +{ + if (!isStreamValid() || !m_appSrc) + return; + + if (m_dataRequested && !m_enoughData) { + qint64 size; + if (m_dataRequestSize == ~0u) + size = qMin(m_stream->bytesAvailable(), queueSize()); + else + size = qMin(m_stream->bytesAvailable(), (qint64)m_dataRequestSize); + + if (size) { + GstBuffer* buffer = gst_buffer_new_and_alloc(size); + + GstMapInfo mapInfo; + gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE); + void* bufferData = mapInfo.data; + + buffer->offset = m_stream->pos(); + qint64 bytesRead = m_stream->read((char*)bufferData, size); + buffer->offset_end = buffer->offset + bytesRead - 1; + + gst_buffer_unmap(buffer, &mapInfo); + + if (bytesRead > 0) { + m_dataRequested = false; + m_enoughData = false; + GstFlowReturn ret = gst_app_src_push_buffer (GST_APP_SRC (element()), buffer); + if (ret == GST_FLOW_ERROR) { + qWarning()<<"appsrc: push buffer error"; + } else if (ret == GST_FLOW_FLUSHING) { + qWarning()<<"appsrc: push buffer wrong state"; + } + } + } else if (!m_sequential) { + sendEOS(); + } + } else if (m_stream->atEnd() && !m_sequential) { + sendEOS(); + } +} + +bool QGstAppSrc::doSeek(qint64 value) +{ + if (isStreamValid()) + return stream()->seek(value); + return false; +} + + +gboolean QGstAppSrc::on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata) +{ + Q_UNUSED(element); + QGstAppSrc *self = reinterpret_cast(userdata); + if (self && self->isStreamValid()) { + if (!self->stream()->isSequential()) + QMetaObject::invokeMethod(self, "doSeek", Qt::AutoConnection, Q_ARG(qint64, arg0)); + } + else + return false; + + return true; +} + +void QGstAppSrc::on_enough_data(GstAppSrc *element, gpointer userdata) +{ + Q_UNUSED(element); + QGstAppSrc *self = reinterpret_cast(userdata); + if (self) + self->enoughData() = true; +} + +void QGstAppSrc::on_need_data(GstAppSrc *element, guint arg0, gpointer userdata) +{ + Q_UNUSED(element); + QGstAppSrc *self = reinterpret_cast(userdata); + if (self) { + self->dataRequested() = true; + self->enoughData() = false; + self->dataRequestSize()= arg0; + QMetaObject::invokeMethod(self, "pushDataToAppSrc", Qt::AutoConnection); + } +} + +void QGstAppSrc::destroy_notify(gpointer data) +{ + Q_UNUSED(data); +} + +void QGstAppSrc::sendEOS() +{ + if (!m_appSrc) + return; + + gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc)); + if (isStreamValid() && !stream()->isSequential()) + stream()->reset(); +} diff --git a/src/multimedia/gstreamer/qgstappsrc_p.h b/src/multimedia/gstreamer/qgstappsrc_p.h new file mode 100644 index 000000000..9c443c0c4 --- /dev/null +++ b/src/multimedia/gstreamer/qgstappsrc_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTAPPSRC_H +#define QGSTAPPSRC_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 +#include +#include + +#include +#include + +#if GST_VERSION_MAJOR < 1 +#include +#endif + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstAppSrc : public QObject +{ + Q_OBJECT +public: + QGstAppSrc(QObject *parent = 0); + ~QGstAppSrc(); + + bool setup(GstElement *); + + void setStream(QIODevice *); + QIODevice *stream() const; + + GstAppSrc *element(); + + qint64 queueSize() const { return m_maxBytes; } + + bool& enoughData() { return m_enoughData; } + bool& dataRequested() { return m_dataRequested; } + unsigned int& dataRequestSize() { return m_dataRequestSize; } + + bool isStreamValid() const + { + return m_stream != 0 && + m_stream->isOpen(); + } + +private slots: + void pushDataToAppSrc(); + bool doSeek(qint64); + void onDataReady(); + + void streamDestroyed(); +private: + static gboolean on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata); + static void on_enough_data(GstAppSrc *element, gpointer userdata); + static void on_need_data(GstAppSrc *element, uint arg0, gpointer userdata); + static void destroy_notify(gpointer data); + + void sendEOS(); + + QIODevice *m_stream = nullptr; + GstAppSrc *m_appSrc = nullptr; + bool m_sequential = false; + GstAppStreamType m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS; + GstAppSrcCallbacks m_callbacks; + qint64 m_maxBytes = 0; + unsigned int m_dataRequestSize = ~0; + bool m_dataRequested = false; + bool m_enoughData = false; + bool m_forceData = false; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstcodecsinfo.cpp b/src/multimedia/gstreamer/qgstcodecsinfo.cpp new file mode 100644 index 000000000..bbf78124d --- /dev/null +++ b/src/multimedia/gstreamer/qgstcodecsinfo.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** 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 "qgstcodecsinfo_p.h" +#include "qgstutils_p.h" +#include + +#include + +static QSet streamTypes(GstElementFactory *factory, GstPadDirection direction) +{ + QSet types; + const GList *pads = gst_element_factory_get_static_pad_templates(factory); + for (const GList *pad = pads; pad; pad = g_list_next(pad)) { + GstStaticPadTemplate *templ = reinterpret_cast(pad->data); + if (templ->direction == direction) { + GstCaps *caps = gst_static_caps_get(&templ->static_caps); + for (uint i = 0; i < gst_caps_get_size(caps); ++i) { + GstStructure *structure = gst_caps_get_structure(caps, i); + types.insert(QString::fromUtf8(gst_structure_get_name(structure))); + } + gst_caps_unref(caps); + } + } + + return types; +} + +QGstCodecsInfo::QGstCodecsInfo(QGstCodecsInfo::ElementType elementType) +{ + updateCodecs(elementType); + for (auto &codec : supportedCodecs()) { + GstElementFactory *factory = gst_element_factory_find(codecElement(codec).constData()); + if (factory) { + GstPadDirection direction = elementType == Muxer ? GST_PAD_SINK : GST_PAD_SRC; + m_streamTypes.insert(codec, streamTypes(factory, direction)); + gst_object_unref(GST_OBJECT(factory)); + } + } +} + +QStringList QGstCodecsInfo::supportedCodecs() const +{ + return m_codecs; +} + +QString QGstCodecsInfo::codecDescription(const QString &codec) const +{ + return m_codecInfo.value(codec).description; +} + +QByteArray QGstCodecsInfo::codecElement(const QString &codec) const + +{ + return m_codecInfo.value(codec).elementName; +} + +QStringList QGstCodecsInfo::codecOptions(const QString &codec) const +{ + QStringList options; + + QByteArray elementName = m_codecInfo.value(codec).elementName; + if (elementName.isEmpty()) + return options; + + GstElement *element = gst_element_factory_make(elementName, nullptr); + if (element) { + guint numProperties; + GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(element), + &numProperties); + for (guint j = 0; j < numProperties; ++j) { + GParamSpec *property = properties[j]; + // ignore some properties + if (strcmp(property->name, "name") == 0 || strcmp(property->name, "parent") == 0) + continue; + + options.append(QLatin1String(property->name)); + } + g_free(properties); + gst_object_unref(element); + } + + return options; +} + +void QGstCodecsInfo::updateCodecs(ElementType elementType) +{ + m_codecs.clear(); + m_codecInfo.clear(); + + GList *elements = elementFactories(elementType); + + QSet fakeEncoderMimeTypes; + fakeEncoderMimeTypes << "unknown/unknown" + << "audio/x-raw-int" << "audio/x-raw-float" + << "video/x-raw-yuv" << "video/x-raw-rgb"; + + QSet fieldsToAdd; + fieldsToAdd << "mpegversion" << "layer" << "layout" << "raversion" + << "wmaversion" << "wmvversion" << "variant" << "systemstream"; + + GList *element = elements; + while (element) { + GstElementFactory *factory = (GstElementFactory *)element->data; + element = element->next; + + const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); + while (padTemplates) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; + padTemplates = padTemplates->next; + + if (padTemplate->direction == GST_PAD_SRC) { + GstCaps *caps = gst_static_caps_get(&padTemplate->static_caps); + for (uint i=0; i::const_iterator it = m_codecInfo.constFind(codec); + if (it == m_codecInfo.constEnd() || it->rank < rank) { + if (it == m_codecInfo.constEnd()) + m_codecs.append(codec); + + CodecInfo info; + info.elementName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); + + gchar *description = gst_pb_utils_get_codec_description(newCaps); + info.description = QString::fromUtf8(description); + if (description) + g_free(description); + + info.rank = rank; + + m_codecInfo.insert(codec, info); + } + + gst_caps_unref(newCaps); + } + gst_caps_unref(caps); + } + } + } + + gst_plugin_feature_list_free(elements); +} + +GList *QGstCodecsInfo::elementFactories(ElementType elementType) const +{ + GstElementFactoryListType gstElementType = 0; + switch (elementType) { + case AudioEncoder: + gstElementType = GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER; + break; + case VideoEncoder: + gstElementType = GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER; + break; + case Muxer: + gstElementType = GST_ELEMENT_FACTORY_TYPE_MUXER; + break; + } + + GList *list = gst_element_factory_list_get_elements(gstElementType, GST_RANK_MARGINAL); + if (elementType == AudioEncoder) { + // Manually add "audioconvert" to the list + // to allow linking with various containers. + auto factory = gst_element_factory_find("audioconvert"); + if (factory) + list = g_list_prepend(list, factory); + } + + return list; +} + +QSet QGstCodecsInfo::supportedStreamTypes(const QString &codec) const +{ + return m_streamTypes.value(codec); +} + +QStringList QGstCodecsInfo::supportedCodecs(const QSet &types) const +{ + QStringList result; + for (auto &candidate : supportedCodecs()) { + auto candidateTypes = supportedStreamTypes(candidate); + if (candidateTypes.intersects(types)) + result << candidate; + } + + return result; +} diff --git a/src/multimedia/gstreamer/qgstcodecsinfo_p.h b/src/multimedia/gstreamer/qgstcodecsinfo_p.h new file mode 100644 index 000000000..7f4415e69 --- /dev/null +++ b/src/multimedia/gstreamer/qgstcodecsinfo_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTCODECSINFO_H +#define QGSTCODECSINFO_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 +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstCodecsInfo +{ +public: + enum ElementType { AudioEncoder, VideoEncoder, Muxer }; + + struct CodecInfo { + QString description; + QByteArray elementName; + GstRank rank; + }; + + QGstCodecsInfo(ElementType elementType); + + QStringList supportedCodecs() const; + QString codecDescription(const QString &codec) const; + QByteArray codecElement(const QString &codec) const; + QStringList codecOptions(const QString &codec) const; + QSet supportedStreamTypes(const QString &codec) const; + QStringList supportedCodecs(const QSet &types) const; + +private: + void updateCodecs(ElementType elementType); + GList *elementFactories(ElementType elementType) const; + + QStringList m_codecs; + QMap m_codecInfo; + QMap> m_streamTypes; +}; + +Q_DECLARE_TYPEINFO(QGstCodecsInfo::CodecInfo, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstreameraudioinputselector.cpp b/src/multimedia/gstreamer/qgstreameraudioinputselector.cpp new file mode 100644 index 000000000..dede1f661 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreameraudioinputselector.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** 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 +#include "qgstreameraudioinputselector_p.h" + +#include +#include + +#include + +#include "qgstutils_p.h" + +QGstreamerAudioInputSelector::QGstreamerAudioInputSelector(QObject *parent) + :QAudioInputSelectorControl(parent) +{ + update(); +} + +QGstreamerAudioInputSelector::~QGstreamerAudioInputSelector() +{ +} + +QList QGstreamerAudioInputSelector::availableInputs() const +{ + return m_names; +} + +QString QGstreamerAudioInputSelector::inputDescription(const QString& name) const +{ + QString desc; + + for (int i = 0; i < m_names.size(); i++) { + if (m_names.at(i).compare(name) == 0) { + desc = m_descriptions.at(i); + break; + } + } + return desc; +} + +QString QGstreamerAudioInputSelector::defaultInput() const +{ + return m_defaultInput; +} + +QString QGstreamerAudioInputSelector::activeInput() const +{ + return m_audioInput; +} + +void QGstreamerAudioInputSelector::setActiveInput(const QString& name) +{ + if (m_audioInput.compare(name) != 0) { + m_audioInput = name; + emit activeInputChanged(name); + } +} + +void QGstreamerAudioInputSelector::update() +{ + QGstUtils::initializeGst(); + + m_names.clear(); + m_descriptions.clear(); + + const auto sources = QGstUtils::audioSources(); + for (auto *d : sources) { + auto *properties = gst_device_get_properties(d); + if (properties) { + auto *desc = gst_device_get_display_name(d); + QString description = QString::fromUtf8(desc); + g_free(desc); + if (description.contains(u"Monitor")) // ### is there a better way to skip those? + continue; + m_descriptions << description; + + auto *name = gst_structure_get_string(properties, "sysfs.path"); + m_names << QString::fromLatin1(name); + gboolean def; + if (gst_structure_get_boolean(properties, "is-default", &def) && def) + m_defaultInput = QString::fromLatin1(name); + + gst_structure_free(properties); + } + } + + if (m_names.size() > 0) + m_audioInput = m_names.at(0); +} diff --git a/src/multimedia/gstreamer/qgstreameraudioinputselector_p.h b/src/multimedia/gstreamer/qgstreameraudioinputselector_p.h new file mode 100644 index 000000000..3b258ba3a --- /dev/null +++ b/src/multimedia/gstreamer/qgstreameraudioinputselector_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERAUDIOINPUTSELECTOR_H +#define QGSTREAMERAUDIOINPUTSELECTOR_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 +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerAudioInputSelector : public QAudioInputSelectorControl +{ +Q_OBJECT +public: + QGstreamerAudioInputSelector(QObject *parent); + ~QGstreamerAudioInputSelector(); + + QList availableInputs() const override; + QString inputDescription(const QString &name) const override; + QString defaultInput() const override; + QString activeInput() const override; + +public Q_SLOTS: + void setActiveInput(const QString &name) override; + +private: + void update(); + + QString m_defaultInput; + QString m_audioInput; + QList m_names; + QList m_descriptions; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERAUDIOINPUTSELECTOR_H diff --git a/src/multimedia/gstreamer/qgstreameraudioprobecontrol.cpp b/src/multimedia/gstreamer/qgstreameraudioprobecontrol.cpp new file mode 100644 index 000000000..8b0415bde --- /dev/null +++ b/src/multimedia/gstreamer/qgstreameraudioprobecontrol.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 "qgstreameraudioprobecontrol_p.h" +#include + +QGstreamerAudioProbeControl::QGstreamerAudioProbeControl(QObject *parent) + : QMediaAudioProbeControl(parent) +{ +} + +QGstreamerAudioProbeControl::~QGstreamerAudioProbeControl() +{ +} + +void QGstreamerAudioProbeControl::probeCaps(GstCaps *caps) +{ + QAudioFormat format = QGstUtils::audioFormatForCaps(caps); + + QMutexLocker locker(&m_bufferMutex); + m_format = format; +} + +bool QGstreamerAudioProbeControl::probeBuffer(GstBuffer *buffer) +{ + qint64 position = GST_BUFFER_TIMESTAMP(buffer); + position = position >= 0 + ? position / G_GINT64_CONSTANT(1000) // microseconds + : -1; + + QByteArray data; + GstMapInfo info; + if (gst_buffer_map(buffer, &info, GST_MAP_READ)) { + data = QByteArray(reinterpret_cast(info.data), info.size); + gst_buffer_unmap(buffer, &info); + } else { + return true; + } + + QMutexLocker locker(&m_bufferMutex); + if (m_format.isValid()) { + if (!m_pendingBuffer.isValid()) + QMetaObject::invokeMethod(this, "bufferProbed", Qt::QueuedConnection); + m_pendingBuffer = QAudioBuffer(data, m_format, position); + } + + return true; +} + +void QGstreamerAudioProbeControl::bufferProbed() +{ + QAudioBuffer audioBuffer; + { + QMutexLocker locker(&m_bufferMutex); + if (!m_pendingBuffer.isValid()) + return; + audioBuffer = m_pendingBuffer; + m_pendingBuffer = QAudioBuffer(); + } + emit audioBufferProbed(audioBuffer); +} diff --git a/src/multimedia/gstreamer/qgstreameraudioprobecontrol_p.h b/src/multimedia/gstreamer/qgstreameraudioprobecontrol_p.h new file mode 100644 index 000000000..b641929cd --- /dev/null +++ b/src/multimedia/gstreamer/qgstreameraudioprobecontrol_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERAUDIOPROBECONTROL_H +#define QGSTREAMERAUDIOPROBECONTROL_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 +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerAudioProbeControl + : public QMediaAudioProbeControl + , public QGstreamerBufferProbe + , public QSharedData +{ + Q_OBJECT +public: + explicit QGstreamerAudioProbeControl(QObject *parent); + virtual ~QGstreamerAudioProbeControl(); + +protected: + void probeCaps(GstCaps *caps) override; + bool probeBuffer(GstBuffer *buffer) override; + +private slots: + void bufferProbed(); + +private: + QAudioBuffer m_pendingBuffer; + QAudioFormat m_format; + QMutex m_bufferMutex; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERAUDIOPROBECONTROL_H diff --git a/src/multimedia/gstreamer/qgstreamerbufferprobe.cpp b/src/multimedia/gstreamer/qgstreamerbufferprobe.cpp new file mode 100644 index 000000000..230807466 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerbufferprobe.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla 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 "qgstreamerbufferprobe_p.h" +#include "qgstutils_p.h" + +QT_BEGIN_NAMESPACE + +QGstreamerBufferProbe::QGstreamerBufferProbe(Flags flags) + : m_flags(flags) +{ +} + +QGstreamerBufferProbe::~QGstreamerBufferProbe() +{ +} + +void QGstreamerBufferProbe::addProbeToPad(GstPad *pad, bool downstream) +{ + if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) { + probeCaps(caps); + gst_caps_unref(caps); + } + if (m_flags & ProbeCaps) { + m_capsProbeId = gst_pad_add_probe( + pad, + downstream + ? GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM + : GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, + capsProbe, + this, + nullptr); + } + if (m_flags & ProbeBuffers) { + m_bufferProbeId = gst_pad_add_probe( + pad, GST_PAD_PROBE_TYPE_BUFFER, bufferProbe, this, nullptr); + } +} + +void QGstreamerBufferProbe::removeProbeFromPad(GstPad *pad) +{ + if (m_capsProbeId != -1) { + gst_pad_remove_probe(pad, m_capsProbeId); + m_capsProbeId = -1; + } + if (m_bufferProbeId != -1) { + gst_pad_remove_probe(pad, m_bufferProbeId); + m_bufferProbeId = -1; + } +} + +void QGstreamerBufferProbe::probeCaps(GstCaps *) +{ +} + +bool QGstreamerBufferProbe::probeBuffer(GstBuffer *) +{ + return true; +} + +GstPadProbeReturn QGstreamerBufferProbe::capsProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data) +{ + QGstreamerBufferProbe * const control = static_cast(user_data); + + if (GstEvent * const event = gst_pad_probe_info_get_event(info)) { + if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS) { + GstCaps *caps; + gst_event_parse_caps(event, &caps); + + control->probeCaps(caps); + } + } + return GST_PAD_PROBE_OK; +} + +GstPadProbeReturn QGstreamerBufferProbe::bufferProbe( + GstPad *, GstPadProbeInfo *info, gpointer user_data) +{ + QGstreamerBufferProbe * const control = static_cast(user_data); + if (GstBuffer * const buffer = gst_pad_probe_info_get_buffer(info)) + return control->probeBuffer(buffer) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP; + return GST_PAD_PROBE_OK; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/gstreamer/qgstreamerbufferprobe_p.h b/src/multimedia/gstreamer/qgstreamerbufferprobe_p.h new file mode 100644 index 000000000..5d66f7320 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerbufferprobe_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla 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 QGSTREAMERBUFFERPROBE_H +#define QGSTREAMERBUFFERPROBE_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 +#include + +#include + + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerBufferProbe +{ +public: + enum Flags + { + ProbeCaps = 0x01, + ProbeBuffers = 0x02, + ProbeAll = ProbeCaps | ProbeBuffers + }; + + explicit QGstreamerBufferProbe(Flags flags = ProbeAll); + virtual ~QGstreamerBufferProbe(); + + void addProbeToPad(GstPad *pad, bool downstream = true); + void removeProbeFromPad(GstPad *pad); + +protected: + virtual void probeCaps(GstCaps *caps); + virtual bool probeBuffer(GstBuffer *buffer); + +private: + static GstPadProbeReturn capsProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); + static GstPadProbeReturn bufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); + int m_capsProbeId = -1; + int m_bufferProbeId = -1; + const Flags m_flags; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERAUDIOPROBECONTROL_H diff --git a/src/multimedia/gstreamer/qgstreamerbushelper.cpp b/src/multimedia/gstreamer/qgstreamerbushelper.cpp new file mode 100644 index 000000000..2eb038dfa --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerbushelper.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include + +#include "qgstreamerbushelper_p.h" + +QT_BEGIN_NAMESPACE + + +class QGstreamerBusHelperPrivate : public QObject +{ + Q_OBJECT +public: + QGstreamerBusHelperPrivate(QGstreamerBusHelper *parent, GstBus* bus) : + QObject(parent), + m_tag(0), + m_bus(bus), + m_helper(parent), + m_intervalTimer(nullptr) + { + // glib event loop can be disabled either by env variable or QT_NO_GLIB define, so check the dispacher + QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); + const bool hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); + if (!hasGlib) { + m_intervalTimer = new QTimer(this); + m_intervalTimer->setInterval(250); + connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); + m_intervalTimer->start(); + } else { + m_tag = gst_bus_add_watch_full(bus, G_PRIORITY_DEFAULT, busCallback, this, nullptr); + } + } + + ~QGstreamerBusHelperPrivate() + { + m_helper = 0; + delete m_intervalTimer; + + if (m_tag) + gst_bus_remove_watch(m_bus); + } + + GstBus* bus() const { return m_bus; } + +private slots: + void interval() + { + GstMessage* message; + while ((message = gst_bus_poll(m_bus, GST_MESSAGE_ANY, 0)) != 0) { + processMessage(message); + gst_message_unref(message); + } + } + +private: + void processMessage(GstMessage* message) + { + QGstreamerMessage msg(message); + doProcessMessage(msg); + } + + void queueMessage(GstMessage* message) + { + QGstreamerMessage msg(message); + QMetaObject::invokeMethod(this, "doProcessMessage", Qt::QueuedConnection, + Q_ARG(QGstreamerMessage, msg)); + } + + static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data) + { + Q_UNUSED(bus); + reinterpret_cast(data)->queueMessage(message); + return TRUE; + } + + guint m_tag; + GstBus* m_bus; + QGstreamerBusHelper* m_helper; + QTimer* m_intervalTimer; + +private slots: + void doProcessMessage(const QGstreamerMessage& msg) + { + for (QGstreamerBusMessageFilter *filter : qAsConst(busFilters)) { + if (filter->processBusMessage(msg)) + break; + } + emit m_helper->message(msg); + } + +public: + QMutex filterMutex; + QList syncFilters; + QList busFilters; +}; + + +static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstreamerBusHelperPrivate *d) +{ + Q_UNUSED(bus); + QMutexLocker lock(&d->filterMutex); + + for (QGstreamerSyncMessageFilter *filter : qAsConst(d->syncFilters)) { + if (filter->processSyncMessage(QGstreamerMessage(message))) { + gst_message_unref(message); + return GST_BUS_DROP; + } + } + + return GST_BUS_PASS; +} + + +/*! + \class QGstreamerBusHelper + \internal +*/ + +QGstreamerBusHelper::QGstreamerBusHelper(GstBus* bus, QObject* parent): + QObject(parent) +{ + d = new QGstreamerBusHelperPrivate(this, bus); + gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, d, 0); + gst_object_ref(GST_OBJECT(bus)); +} + +QGstreamerBusHelper::~QGstreamerBusHelper() +{ + gst_bus_set_sync_handler(d->bus(), 0, 0, 0); + gst_object_unref(GST_OBJECT(d->bus())); +} + +void QGstreamerBusHelper::installMessageFilter(QObject *filter) +{ + auto syncFilter = qobject_cast(filter); + if (syncFilter) { + QMutexLocker lock(&d->filterMutex); + if (!d->syncFilters.contains(syncFilter)) + d->syncFilters.append(syncFilter); + } + + auto busFilter = qobject_cast(filter); + if (busFilter && !d->busFilters.contains(busFilter)) + d->busFilters.append(busFilter); +} + +void QGstreamerBusHelper::removeMessageFilter(QObject *filter) +{ + auto syncFilter = qobject_cast(filter); + if (syncFilter) { + QMutexLocker lock(&d->filterMutex); + d->syncFilters.removeAll(syncFilter); + } + + auto busFilter = qobject_cast(filter); + if (busFilter) + d->busFilters.removeAll(busFilter); +} + +QT_END_NAMESPACE + +#include "qgstreamerbushelper.moc" diff --git a/src/multimedia/gstreamer/qgstreamerbushelper_p.h b/src/multimedia/gstreamer/qgstreamerbushelper_p.h new file mode 100644 index 000000000..01d3ed826 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerbushelper_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERBUSHELPER_P_H +#define QGSTREAMERBUSHELPER_P_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 +#include + +#include "qgstreamermessage_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGstreamerSyncMessageFilter { +public: + //returns true if message was processed and should be dropped, false otherwise + virtual bool processSyncMessage(const QGstreamerMessage &message) = 0; +}; +#define QGstreamerSyncMessageFilter_iid "org.qt-project.qt.gstreamersyncmessagefilter/5.0" +Q_DECLARE_INTERFACE(QGstreamerSyncMessageFilter, QGstreamerSyncMessageFilter_iid) + + +class QGstreamerBusMessageFilter { +public: + //returns true if message was processed and should be dropped, false otherwise + virtual bool processBusMessage(const QGstreamerMessage &message) = 0; +}; +#define QGstreamerBusMessageFilter_iid "org.qt-project.qt.gstreamerbusmessagefilter/5.0" +Q_DECLARE_INTERFACE(QGstreamerBusMessageFilter, QGstreamerBusMessageFilter_iid) + + +class QGstreamerBusHelperPrivate; + +class Q_MULTIMEDIA_EXPORT QGstreamerBusHelper : public QObject +{ + Q_OBJECT + friend class QGstreamerBusHelperPrivate; + +public: + QGstreamerBusHelper(GstBus* bus, QObject* parent = 0); + ~QGstreamerBusHelper(); + + void installMessageFilter(QObject *filter); + void removeMessageFilter(QObject *filter); + +signals: + void message(QGstreamerMessage const& message); + +private: + QGstreamerBusHelperPrivate *d = nullptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstreamermessage.cpp b/src/multimedia/gstreamer/qgstreamermessage.cpp new file mode 100644 index 000000000..7191565e1 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamermessage.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 + +#include "qgstreamermessage_p.h" + +QT_BEGIN_NAMESPACE + +static int wuchi = qRegisterMetaType(); + + +/*! + \class QGstreamerMessage + \internal +*/ + +QGstreamerMessage::QGstreamerMessage(GstMessage* message): + m_message(message) +{ + gst_message_ref(m_message); +} + +QGstreamerMessage::QGstreamerMessage(QGstreamerMessage const& m): + m_message(m.m_message) +{ + gst_message_ref(m_message); +} + + +QGstreamerMessage::~QGstreamerMessage() +{ + if (m_message != 0) + gst_message_unref(m_message); +} + +GstMessage* QGstreamerMessage::rawMessage() const +{ + return m_message; +} + +QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs) +{ + if (rhs.m_message != m_message) { + if (rhs.m_message != 0) + gst_message_ref(rhs.m_message); + + if (m_message != 0) + gst_message_unref(m_message); + + m_message = rhs.m_message; + } + + return *this; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/gstreamer/qgstreamermessage_p.h b/src/multimedia/gstreamer/qgstreamermessage_p.h new file mode 100644 index 000000000..d552731d2 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamermessage_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMESSAGE_P_H +#define QGSTREAMERMESSAGE_P_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 +#include + +#include + +QT_BEGIN_NAMESPACE + +// Required for QDoc workaround +class QString; + +class Q_MULTIMEDIA_EXPORT QGstreamerMessage +{ +public: + QGstreamerMessage() = default; + QGstreamerMessage(GstMessage* message); + QGstreamerMessage(QGstreamerMessage const& m); + ~QGstreamerMessage(); + + GstMessage* rawMessage() const; + + QGstreamerMessage& operator=(QGstreamerMessage const& rhs); + +private: + GstMessage* m_message = nullptr; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGstreamerMessage); + +#endif diff --git a/src/multimedia/gstreamer/qgstreamerplayercontrol.cpp b/src/multimedia/gstreamer/qgstreamerplayercontrol.cpp new file mode 100644 index 000000000..8ad2e0e93 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerplayercontrol.cpp @@ -0,0 +1,527 @@ +/**************************************************************************** +** +** 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 +#include + +#include +#include +#include +#include + +#include +#include +#include + +//#define DEBUG_PLAYBIN + +QT_BEGIN_NAMESPACE + +QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent) + : QMediaPlayerControl(parent) + , m_session(session) +{ + connect(m_session, &QGstreamerPlayerSession::positionChanged, this, &QGstreamerPlayerControl::positionChanged); + connect(m_session, &QGstreamerPlayerSession::durationChanged, this, &QGstreamerPlayerControl::durationChanged); + connect(m_session, &QGstreamerPlayerSession::mutedStateChanged, this, &QGstreamerPlayerControl::mutedChanged); + connect(m_session, &QGstreamerPlayerSession::volumeChanged, this, &QGstreamerPlayerControl::volumeChanged); + connect(m_session, &QGstreamerPlayerSession::stateChanged, this, &QGstreamerPlayerControl::updateSessionState); + connect(m_session, &QGstreamerPlayerSession::bufferingProgressChanged, this, &QGstreamerPlayerControl::setBufferProgress); + connect(m_session, &QGstreamerPlayerSession::playbackFinished, this, &QGstreamerPlayerControl::processEOS); + connect(m_session, &QGstreamerPlayerSession::audioAvailableChanged, this, &QGstreamerPlayerControl::audioAvailableChanged); + connect(m_session, &QGstreamerPlayerSession::videoAvailableChanged, this, &QGstreamerPlayerControl::videoAvailableChanged); + connect(m_session, &QGstreamerPlayerSession::seekableChanged, this, &QGstreamerPlayerControl::seekableChanged); + connect(m_session, &QGstreamerPlayerSession::error, this, &QGstreamerPlayerControl::error); + connect(m_session, &QGstreamerPlayerSession::invalidMedia, this, &QGstreamerPlayerControl::handleInvalidMedia); + connect(m_session, &QGstreamerPlayerSession::playbackRateChanged, this, &QGstreamerPlayerControl::playbackRateChanged); +} + +QGstreamerPlayerControl::~QGstreamerPlayerControl() +{ +} + +qint64 QGstreamerPlayerControl::position() const +{ + if (m_mediaStatus == QMediaPlayer::EndOfMedia) + return m_session->duration(); + + return m_pendingSeekPosition != -1 ? m_pendingSeekPosition : m_session->position(); +} + +qint64 QGstreamerPlayerControl::duration() const +{ + return m_session->duration(); +} + +QMediaPlayer::State QGstreamerPlayerControl::state() const +{ + return m_currentState; +} + +QMediaPlayer::MediaStatus QGstreamerPlayerControl::mediaStatus() const +{ + return m_mediaStatus; +} + +int QGstreamerPlayerControl::bufferStatus() const +{ + if (m_bufferProgress == -1) + return m_session->state() == QMediaPlayer::StoppedState ? 0 : 100; + + return m_bufferProgress; +} + +int QGstreamerPlayerControl::volume() const +{ + return m_session->volume(); +} + +bool QGstreamerPlayerControl::isMuted() const +{ + return m_session->isMuted(); +} + +bool QGstreamerPlayerControl::isSeekable() const +{ + return m_session->isSeekable(); +} + +QMediaTimeRange QGstreamerPlayerControl::availablePlaybackRanges() const +{ + return m_session->availablePlaybackRanges(); +} + +qreal QGstreamerPlayerControl::playbackRate() const +{ + return m_session->playbackRate(); +} + +void QGstreamerPlayerControl::setPlaybackRate(qreal rate) +{ + m_session->setPlaybackRate(rate); +} + +void QGstreamerPlayerControl::setPosition(qint64 pos) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << pos/1000.0; +#endif + + pushState(); + + if (m_mediaStatus == QMediaPlayer::EndOfMedia) { + m_mediaStatus = QMediaPlayer::LoadedMedia; + } + + if (m_currentState == QMediaPlayer::StoppedState) { + m_pendingSeekPosition = pos; + emit positionChanged(m_pendingSeekPosition); + } else if (m_session->isSeekable()) { + m_session->showPrerollFrames(true); + m_session->seek(pos); + m_pendingSeekPosition = -1; + } else if (m_session->state() == QMediaPlayer::StoppedState) { + m_pendingSeekPosition = pos; + emit positionChanged(m_pendingSeekPosition); + } else if (m_pendingSeekPosition != -1) { + m_pendingSeekPosition = -1; + emit positionChanged(m_pendingSeekPosition); + } + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::play() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + //m_userRequestedState is needed to know that we need to resume playback when resource-policy + //regranted the resources after lost, since m_currentState will become paused when resources are + //lost. + m_userRequestedState = QMediaPlayer::PlayingState; + playOrPause(QMediaPlayer::PlayingState); +} + +void QGstreamerPlayerControl::pause() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + m_userRequestedState = QMediaPlayer::PausedState; + // If the playback has not been started yet but pause is requested. + // Seek to the beginning to show first frame. + if (m_pendingSeekPosition == -1 && m_session->position() == 0) + m_pendingSeekPosition = 0; + + playOrPause(QMediaPlayer::PausedState); +} + +void QGstreamerPlayerControl::playOrPause(QMediaPlayer::State newState) +{ + if (m_mediaStatus == QMediaPlayer::NoMedia) + return; + + pushState(); + + if (m_setMediaPending) { + m_mediaStatus = QMediaPlayer::LoadingMedia; + setMedia(m_currentResource, m_stream); + } + + if (m_mediaStatus == QMediaPlayer::EndOfMedia && m_pendingSeekPosition == -1) { + m_pendingSeekPosition = 0; + } + + // show prerolled frame if switching from stopped state + if (m_pendingSeekPosition == -1) { + m_session->showPrerollFrames(true); + } else if (m_session->state() == QMediaPlayer::StoppedState) { + // Don't evaluate the next two conditions. + } else if (m_session->isSeekable()) { + m_session->pause(); + m_session->showPrerollFrames(true); + m_session->seek(m_pendingSeekPosition); + m_pendingSeekPosition = -1; + } else { + m_pendingSeekPosition = -1; + } + + bool ok = false; + + //To prevent displaying the first video frame when playback is resumed + //the pipeline is paused instead of playing, seeked to requested position, + //and after seeking is finished (position updated) playback is restarted + //with show-preroll-frame enabled. + if (newState == QMediaPlayer::PlayingState && m_pendingSeekPosition == -1) + ok = m_session->play(); + else + ok = m_session->pause(); + + if (!ok) + newState = QMediaPlayer::StoppedState; + + if (m_mediaStatus == QMediaPlayer::InvalidMedia) + m_mediaStatus = QMediaPlayer::LoadingMedia; + + m_currentState = newState; + + if (m_mediaStatus == QMediaPlayer::EndOfMedia || m_mediaStatus == QMediaPlayer::LoadedMedia) { + if (m_bufferProgress == -1 || m_bufferProgress == 100) + m_mediaStatus = QMediaPlayer::BufferedMedia; + else + m_mediaStatus = QMediaPlayer::BufferingMedia; + } + + popAndNotifyState(); + + emit positionChanged(position()); +} + +void QGstreamerPlayerControl::stop() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + m_userRequestedState = QMediaPlayer::StoppedState; + + pushState(); + + if (m_currentState != QMediaPlayer::StoppedState) { + m_currentState = QMediaPlayer::StoppedState; + m_session->showPrerollFrames(false); // stop showing prerolled frames in stop state + // Since gst is not going to send GST_STATE_PAUSED + // when pipeline is already paused, + // needs to update media status directly. + if (m_session->state() == QMediaPlayer::PausedState) + updateMediaStatus(); + else + m_session->pause(); + + if (m_mediaStatus != QMediaPlayer::EndOfMedia) { + m_pendingSeekPosition = 0; + emit positionChanged(position()); + } + } + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +void QGstreamerPlayerControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +QMediaContent QGstreamerPlayerControl::media() const +{ + return m_currentResource; +} + +const QIODevice *QGstreamerPlayerControl::mediaStream() const +{ + return m_stream; +} + +void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + + pushState(); + + m_currentState = QMediaPlayer::StoppedState; + QMediaContent oldMedia = m_currentResource; + m_pendingSeekPosition = -1; + m_session->showPrerollFrames(false); // do not show prerolled frames until pause() or play() explicitly called + m_setMediaPending = false; + + m_session->stop(); + + bool userStreamValid = false; + + if (m_bufferProgress != -1) { + m_bufferProgress = -1; + emit bufferStatusChanged(0); + } + + m_currentResource = content; + m_stream = stream; + + QNetworkRequest request = content.request(); + + if (m_stream) + userStreamValid = stream->isOpen() && m_stream->isReadable(); + +#if !QT_CONFIG(gstreamer_app) + m_session->loadFromUri(request); +#else + if (m_stream) { + if (userStreamValid){ + m_session->loadFromStream(request, m_stream); + } else { + m_mediaStatus = QMediaPlayer::InvalidMedia; + emit error(QMediaPlayer::FormatError, tr("Attempting to play invalid user stream")); + popAndNotifyState(); + return; + } + } else + m_session->loadFromUri(request); +#endif + +#if QT_CONFIG(gstreamer_app) + if (!request.url().isEmpty() || userStreamValid) { +#else + if (!request.url().isEmpty()) { +#endif + m_mediaStatus = QMediaPlayer::LoadingMedia; + m_session->pause(); + } else { + m_mediaStatus = QMediaPlayer::NoMedia; + setBufferProgress(0); + } + + if (m_currentResource != oldMedia) + emit mediaChanged(m_currentResource); + + emit positionChanged(position()); + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::setVideoOutput(QObject *output) +{ + m_session->setVideoRenderer(output); +} + +bool QGstreamerPlayerControl::isAudioAvailable() const +{ + return m_session->isAudioAvailable(); +} + +bool QGstreamerPlayerControl::isVideoAvailable() const +{ + return m_session->isVideoAvailable(); +} + +void QGstreamerPlayerControl::updateSessionState(QMediaPlayer::State state) +{ + pushState(); + + if (state == QMediaPlayer::StoppedState) { + m_session->showPrerollFrames(false); + m_currentState = QMediaPlayer::StoppedState; + } + + if (state == QMediaPlayer::PausedState && m_currentState != QMediaPlayer::StoppedState) { + if (m_pendingSeekPosition != -1 && m_session->isSeekable()) { + m_session->showPrerollFrames(true); + m_session->seek(m_pendingSeekPosition); + } + m_pendingSeekPosition = -1; + + if (m_currentState == QMediaPlayer::PlayingState) { + if (m_bufferProgress == -1 || m_bufferProgress == 100) + m_session->play(); + } + } + + updateMediaStatus(); + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::updateMediaStatus() +{ + //EndOfMedia status should be kept, until reset by pause, play or setMedia + if (m_mediaStatus == QMediaPlayer::EndOfMedia) + return; + + pushState(); + QMediaPlayer::MediaStatus oldStatus = m_mediaStatus; + + switch (m_session->state()) { + case QMediaPlayer::StoppedState: + if (m_currentResource.isNull()) + m_mediaStatus = QMediaPlayer::NoMedia; + else if (oldStatus != QMediaPlayer::InvalidMedia) + m_mediaStatus = QMediaPlayer::LoadingMedia; + break; + + case QMediaPlayer::PlayingState: + case QMediaPlayer::PausedState: + if (m_currentState == QMediaPlayer::StoppedState) { + m_mediaStatus = QMediaPlayer::LoadedMedia; + } else { + if (m_bufferProgress == -1 || m_bufferProgress == 100) + m_mediaStatus = QMediaPlayer::BufferedMedia; + else + m_mediaStatus = QMediaPlayer::StalledMedia; + } + break; + } + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::processEOS() +{ + pushState(); + m_mediaStatus = QMediaPlayer::EndOfMedia; + emit positionChanged(position()); + m_session->endOfMediaReset(); + + if (m_currentState != QMediaPlayer::StoppedState) { + m_currentState = QMediaPlayer::StoppedState; + m_session->showPrerollFrames(false); // stop showing prerolled frames in stop state + } + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::setBufferProgress(int progress) +{ + if (m_bufferProgress == progress || m_mediaStatus == QMediaPlayer::NoMedia) + return; + +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << progress; +#endif + m_bufferProgress = progress; + + if (m_currentState == QMediaPlayer::PlayingState && + m_bufferProgress == 100 && + m_session->state() != QMediaPlayer::PlayingState) + m_session->play(); + + if (!m_session->isLiveSource() && m_bufferProgress < 100 && + (m_session->state() == QMediaPlayer::PlayingState || + m_session->pendingState() == QMediaPlayer::PlayingState)) + m_session->pause(); + + updateMediaStatus(); + + emit bufferStatusChanged(m_bufferProgress); +} + +void QGstreamerPlayerControl::handleInvalidMedia() +{ + pushState(); + m_mediaStatus = QMediaPlayer::InvalidMedia; + m_currentState = QMediaPlayer::StoppedState; + m_setMediaPending = true; + popAndNotifyState(); +} + +void QGstreamerPlayerControl::pushState() +{ + m_stateStack.push(m_currentState); + m_mediaStatusStack.push(m_mediaStatus); +} + +void QGstreamerPlayerControl::popAndNotifyState() +{ + Q_ASSERT(!m_stateStack.isEmpty()); + + QMediaPlayer::State oldState = m_stateStack.pop(); + QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatusStack.pop(); + + if (m_stateStack.isEmpty()) { + if (m_mediaStatus != oldMediaStatus) { +#ifdef DEBUG_PLAYBIN + qDebug() << "Media status changed:" << m_mediaStatus; +#endif + emit mediaStatusChanged(m_mediaStatus); + } + + if (m_currentState != oldState) { +#ifdef DEBUG_PLAYBIN + qDebug() << "State changed:" << m_currentState; +#endif + emit stateChanged(m_currentState); + } + } +} + +QT_END_NAMESPACE diff --git a/src/multimedia/gstreamer/qgstreamerplayercontrol_p.h b/src/multimedia/gstreamer/qgstreamerplayercontrol_p.h new file mode 100644 index 000000000..875d0153b --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerplayercontrol_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERCONTROL_P_H +#define QGSTREAMERPLAYERCONTROL_P_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 +#include +#include + +QT_BEGIN_NAMESPACE + +class QGstreamerPlayerSession; +class Q_MULTIMEDIA_EXPORT QGstreamerPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT + +public: + QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent = 0); + ~QGstreamerPlayerControl(); + + QGstreamerPlayerSession *session() { return m_session; } + + QMediaPlayer::State state() const override; + QMediaPlayer::MediaStatus mediaStatus() const override; + + qint64 position() const override; + qint64 duration() const override; + + int bufferStatus() const override; + + int volume() const override; + bool isMuted() const override; + + bool isAudioAvailable() const override; + bool isVideoAvailable() const override; + void setVideoOutput(QObject *output); + + bool isSeekable() const override; + QMediaTimeRange availablePlaybackRanges() const override; + + qreal playbackRate() const override; + void setPlaybackRate(qreal rate) override; + + QMediaContent media() const override; + const QIODevice *mediaStream() const override; + void setMedia(const QMediaContent&, QIODevice *) override; + +public Q_SLOTS: + void setPosition(qint64 pos) override; + + void play() override; + void pause() override; + void stop() override; + + void setVolume(int volume) override; + void setMuted(bool muted) override; + +private Q_SLOTS: + void updateSessionState(QMediaPlayer::State state); + void updateMediaStatus(); + void processEOS(); + void setBufferProgress(int progress); + + void handleInvalidMedia(); + +private: + void playOrPause(QMediaPlayer::State state); + + void pushState(); + void popAndNotifyState(); + + QGstreamerPlayerSession *m_session = nullptr; + QMediaPlayer::State m_userRequestedState = QMediaPlayer::StoppedState; + QMediaPlayer::State m_currentState = QMediaPlayer::StoppedState; + QMediaPlayer::MediaStatus m_mediaStatus = QMediaPlayer::NoMedia; + QStack m_stateStack; + QStack m_mediaStatusStack; + + int m_bufferProgress = -1; + qint64 m_pendingSeekPosition = -1; + bool m_setMediaPending = false; + QMediaContent m_currentResource; + QIODevice *m_stream = nullptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstreamerplayersession.cpp b/src/multimedia/gstreamer/qgstreamerplayersession.cpp new file mode 100644 index 000000000..2729097b5 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerplayersession.cpp @@ -0,0 +1,1774 @@ +/**************************************************************************** +** +** 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 +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG_PLAYBIN + +QT_BEGIN_NAMESPACE + +static bool usePlaybinVolume() +{ + static enum { Yes, No, Unknown } status = Unknown; + if (status == Unknown) { + QByteArray v = qgetenv("QT_GSTREAMER_USE_PLAYBIN_VOLUME"); + bool value = !v.isEmpty() && v != "0" && v != "false"; + if (value) + status = Yes; + else + status = No; + } + return status == Yes; +} + +typedef enum { + GST_PLAY_FLAG_VIDEO = 0x00000001, + GST_PLAY_FLAG_AUDIO = 0x00000002, + GST_PLAY_FLAG_TEXT = 0x00000004, + GST_PLAY_FLAG_VIS = 0x00000008, + GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, + GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, + GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, + GST_PLAY_FLAG_DOWNLOAD = 0x00000080, + GST_PLAY_FLAG_BUFFERING = 0x000000100 +} GstPlayFlags; + +QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + : QObject(parent) +{ + initPlaybin(); +} + +void QGstreamerPlayerSession::initPlaybin() +{ + m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, nullptr); + if (m_playbin) { + //GST_PLAY_FLAG_NATIVE_VIDEO omits configuration of ffmpegcolorspace and videoscale, + //since those elements are included in the video output bin when necessary. + int flags = GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO; + QByteArray envFlags = qgetenv("QT_GSTREAMER_PLAYBIN_FLAGS"); + if (!envFlags.isEmpty()) { + flags |= envFlags.toInt(); + } + g_object_set(G_OBJECT(m_playbin), "flags", flags, nullptr); + + const QByteArray envAudioSink = qgetenv("QT_GSTREAMER_PLAYBIN_AUDIOSINK"); + GstElement *audioSink = gst_element_factory_make(envAudioSink.isEmpty() ? "autoaudiosink" : envAudioSink, "audiosink"); + if (audioSink) { + if (usePlaybinVolume()) { + m_audioSink = audioSink; + m_volumeElement = m_playbin; + } else { + m_volumeElement = gst_element_factory_make("volume", "volumeelement"); + if (m_volumeElement) { + m_audioSink = gst_bin_new("audio-output-bin"); + + gst_bin_add_many(GST_BIN(m_audioSink), m_volumeElement, audioSink, nullptr); + gst_element_link(m_volumeElement, audioSink); + + GstPad *pad = gst_element_get_static_pad(m_volumeElement, "sink"); + gst_element_add_pad(GST_ELEMENT(m_audioSink), gst_ghost_pad_new("sink", pad)); + gst_object_unref(GST_OBJECT(pad)); + } else { + m_audioSink = audioSink; + m_volumeElement = m_playbin; + } + } + + g_object_set(G_OBJECT(m_playbin), "audio-sink", m_audioSink, nullptr); + addAudioBufferProbe(); + } + } + + static const auto convDesc = qEnvironmentVariable("QT_GSTREAMER_PLAYBIN_CONVERT"); + GError *err = nullptr; + auto convPipeline = !convDesc.isEmpty() ? convDesc.toLatin1().constData() : "identity"; + auto convElement = gst_parse_launch(convPipeline, &err); + if (err) { + qWarning() << "Error:" << convDesc << ":" << QLatin1String(err->message); + g_clear_error(&err); + } + m_videoIdentity = convElement; + + m_nullVideoSink = gst_element_factory_make("fakesink", nullptr); + g_object_set(G_OBJECT(m_nullVideoSink), "sync", true, nullptr); + gst_object_ref(GST_OBJECT(m_nullVideoSink)); + + 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", nullptr); + GstElement *colorConvert = gst_element_factory_make("glcolorconvert", nullptr); + gst_bin_add_many(GST_BIN(m_videoOutputBin), videoOutputSink, colorConvert, m_videoIdentity, m_nullVideoSink, nullptr); + gst_element_link_many(videoOutputSink, colorConvert, m_videoIdentity, nullptr); + } else { + gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_nullVideoSink, nullptr); + } +#else + gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_nullVideoSink, nullptr); +#endif + gst_element_link(m_videoIdentity, m_nullVideoSink); + + m_videoSink = m_nullVideoSink; + + // add ghostpads + 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)); + + if (m_playbin != 0) { + // Sort out messages + setBus(gst_element_get_bus(m_playbin)); + + g_object_set(G_OBJECT(m_playbin), "video-sink", m_videoOutputBin, nullptr); + + g_signal_connect(G_OBJECT(m_playbin), "notify::source", G_CALLBACK(playbinNotifySource), this); + g_signal_connect(G_OBJECT(m_playbin), "element-added", G_CALLBACK(handleElementAdded), this); + + if (usePlaybinVolume()) { + updateVolume(); + updateMuted(); + g_signal_connect(G_OBJECT(m_playbin), "notify::volume", G_CALLBACK(handleVolumeChange), this); + g_signal_connect(G_OBJECT(m_playbin), "notify::mute", G_CALLBACK(handleMutedChange), this); + } + + g_signal_connect(G_OBJECT(m_playbin), "video-changed", G_CALLBACK(handleStreamsChange), this); + g_signal_connect(G_OBJECT(m_playbin), "audio-changed", G_CALLBACK(handleStreamsChange), this); + g_signal_connect(G_OBJECT(m_playbin), "text-changed", G_CALLBACK(handleStreamsChange), this); + +#if QT_CONFIG(gstreamer_app) + g_signal_connect(G_OBJECT(m_playbin), "deep-notify::source", G_CALLBACK(configureAppSrcElement), this); +#endif + + m_pipeline = m_playbin; + gst_object_ref(GST_OBJECT(m_pipeline)); + } +} + +QGstreamerPlayerSession::~QGstreamerPlayerSession() +{ + if (m_pipeline) { + stop(); + + removeVideoBufferProbe(); + removeAudioBufferProbe(); + + delete m_busHelper; + m_busHelper = nullptr; + resetElements(); + } +} + +template +static inline void resetGstObject(T *&obj, T *v = nullptr) +{ + if (obj) + gst_object_unref(GST_OBJECT(obj)); + + obj = v; +} + +void QGstreamerPlayerSession::resetElements() +{ + setBus(nullptr); + resetGstObject(m_playbin); + resetGstObject(m_pipeline); + resetGstObject(m_nullVideoSink); + resetGstObject(m_videoOutputBin); + + m_audioSink = nullptr; + m_volumeElement = nullptr; + m_videoIdentity = nullptr; + m_pendingVideoSink = nullptr; + m_videoSink = nullptr; +} + +GstElement *QGstreamerPlayerSession::playbin() const +{ + return m_playbin; +} + +#if QT_CONFIG(gstreamer_app) +void QGstreamerPlayerSession::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerPlayerSession* self) +{ + Q_UNUSED(object); + Q_UNUSED(pspec); + + if (!self->appsrc()) + return; + + GstElement *appsrc; + g_object_get(orig, "source", &appsrc, nullptr); + + if (!self->appsrc()->setup(appsrc)) + qWarning()<<"Could not setup appsrc element"; + + g_object_unref(G_OBJECT(appsrc)); +} +#endif + +void QGstreamerPlayerSession::loadFromStream(const QNetworkRequest &request, QIODevice *appSrcStream) +{ +#if QT_CONFIG(gstreamer_app) +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + m_request = request; + m_duration = 0; + m_lastPosition = 0; + + if (!m_appSrc) + m_appSrc = new QGstAppSrc(this); + m_appSrc->setStream(appSrcStream); + + if (!parsePipeline() && m_playbin) { + m_tags.clear(); + emit tagsChanged(); + + g_object_set(G_OBJECT(m_playbin), "uri", "appsrc://", nullptr); + + if (!m_streamTypes.isEmpty()) { + m_streamProperties.clear(); + m_streamTypes.clear(); + + emit streamsChanged(); + } + } +#endif +} + +void QGstreamerPlayerSession::loadFromUri(const QNetworkRequest &request) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << request.url(); +#endif + m_request = request; + m_duration = 0; + m_lastPosition = 0; + +#if QT_CONFIG(gstreamer_app) + if (m_appSrc) { + m_appSrc->deleteLater(); + m_appSrc = 0; + } +#endif + + if (!parsePipeline() && m_playbin) { + m_tags.clear(); + emit tagsChanged(); + + g_object_set(G_OBJECT(m_playbin), "uri", m_request.url().toEncoded().constData(), nullptr); + + if (!m_streamTypes.isEmpty()) { + m_streamProperties.clear(); + m_streamTypes.clear(); + + emit streamsChanged(); + } + } +} + +bool QGstreamerPlayerSession::parsePipeline() +{ + if (m_request.url().scheme() != QLatin1String("gst-pipeline")) { + if (!m_playbin) { + resetElements(); + initPlaybin(); + updateVideoRenderer(); + } + return false; + } + + // Set current surface to video sink before creating a pipeline. + auto renderer = qobject_cast(m_videoOutput); + if (renderer) + QGstVideoRendererSink::setSurface(renderer->surface()); + + QString url = m_request.url().toString(QUrl::RemoveScheme); + QString desc = QUrl::fromPercentEncoding(url.toLatin1().constData()); + GError *err = nullptr; + GstElement *pipeline = gst_parse_launch(desc.toLatin1().constData(), &err); + if (err) { + auto errstr = QLatin1String(err->message); + qWarning() << "Error:" << desc << ":" << errstr; + emit error(QMediaPlayer::FormatError, errstr); + g_clear_error(&err); + } + + return setPipeline(pipeline); +} + +static void gst_foreach(GstIterator *it, const std::function &cmp) +{ + GValue value = G_VALUE_INIT; + while (gst_iterator_next (it, &value) == GST_ITERATOR_OK) { + auto child = static_cast(g_value_get_object(&value)); + if (cmp(child)) + break; + } + + gst_iterator_free(it); + g_value_unset(&value); +} + +bool QGstreamerPlayerSession::setPipeline(GstElement *pipeline) +{ + GstBus *bus = pipeline ? gst_element_get_bus(pipeline) : nullptr; + if (!bus) + return false; + + if (m_playbin) + gst_element_set_state(m_playbin, GST_STATE_NULL); + + resetElements(); + setBus(bus); + m_pipeline = pipeline; + + if (m_renderer) { + gst_foreach(gst_bin_iterate_sinks(GST_BIN(pipeline)), + [this](GstElement *child) { + if (qstrcmp(GST_OBJECT_NAME(child), "qtvideosink") == 0) { + m_renderer->setVideoSink(child); + return true; + } + return false; + }); + } + +#if QT_CONFIG(gstreamer_app) + if (m_appSrc) { + gst_foreach(gst_bin_iterate_sources(GST_BIN(pipeline)), + [this](GstElement *child) { + if (qstrcmp(qt_gst_element_get_factory_name(child), "appsrc") == 0) { + m_appSrc->setup(child); + return true; + } + return false; + }); + } +#endif + + emit pipelineChanged(); + return true; +} + +void QGstreamerPlayerSession::setBus(GstBus *bus) +{ + resetGstObject(m_bus, bus); + + // It might still accept gst messages. + if (m_busHelper) + m_busHelper->deleteLater(); + m_busHelper = nullptr; + + if (!m_bus) + return; + + m_busHelper = new QGstreamerBusHelper(m_bus, this); + m_busHelper->installMessageFilter(this); + + if (m_videoOutput) + m_busHelper->installMessageFilter(m_videoOutput); +} + +qint64 QGstreamerPlayerSession::duration() const +{ + return m_duration; +} + +qint64 QGstreamerPlayerSession::position() const +{ + gint64 position = 0; + + if (m_pipeline && qt_gst_element_query_position(m_pipeline, GST_FORMAT_TIME, &position)) + m_lastPosition = position / 1000000; + return m_lastPosition; +} + +qreal QGstreamerPlayerSession::playbackRate() const +{ + return m_playbackRate; +} + +void QGstreamerPlayerSession::setPlaybackRate(qreal rate) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << rate; +#endif + if (!qFuzzyCompare(m_playbackRate, rate)) { + m_playbackRate = rate; + if (m_pipeline && m_seekable) { + qint64 from = rate > 0 ? position() : 0; + qint64 to = rate > 0 ? duration() : position(); + gst_element_seek(m_pipeline, rate, GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_FLUSH), + GST_SEEK_TYPE_SET, from * 1000000, + GST_SEEK_TYPE_SET, to * 1000000); + } + emit playbackRateChanged(m_playbackRate); + } +} + +QMediaTimeRange QGstreamerPlayerSession::availablePlaybackRanges() const +{ + QMediaTimeRange ranges; + + if (duration() <= 0) + return ranges; + + //GST_FORMAT_TIME would be more appropriate, but unfortunately it's not supported. + //with GST_FORMAT_PERCENT media is treated as encoded with constant bitrate. + GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); + + if (!gst_element_query(m_pipeline, query)) { + gst_query_unref(query); + return ranges; + } + + gint64 rangeStart = 0; + gint64 rangeStop = 0; + for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) { + if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop)) + ranges.addInterval(rangeStart * duration() / 100, + rangeStop * duration() / 100); + } + + gst_query_unref(query); + + if (ranges.isEmpty() && !isLiveSource() && isSeekable()) + ranges.addInterval(0, duration()); + +#ifdef DEBUG_PLAYBIN + qDebug() << ranges; +#endif + + return ranges; +} + +int QGstreamerPlayerSession::activeStream(QMediaStreamsControl::StreamType streamType) const +{ + int streamNumber = -1; + if (m_playbin) { + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_object_get(G_OBJECT(m_playbin), "current-audio", &streamNumber, nullptr); + break; + case QMediaStreamsControl::VideoStream: + g_object_get(G_OBJECT(m_playbin), "current-video", &streamNumber, nullptr); + break; + case QMediaStreamsControl::SubPictureStream: + g_object_get(G_OBJECT(m_playbin), "current-text", &streamNumber, nullptr); + break; + default: + break; + } + } + + if (streamNumber >= 0) + streamNumber += m_playbin2StreamOffset.value(streamType,0); + + return streamNumber; +} + +void QGstreamerPlayerSession::setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << streamType << streamNumber; +#endif + + if (streamNumber >= 0) + streamNumber -= m_playbin2StreamOffset.value(streamType,0); + + if (m_playbin) { + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_object_set(G_OBJECT(m_playbin), "current-audio", streamNumber, nullptr); + break; + case QMediaStreamsControl::VideoStream: + g_object_set(G_OBJECT(m_playbin), "current-video", streamNumber, nullptr); + break; + case QMediaStreamsControl::SubPictureStream: + g_object_set(G_OBJECT(m_playbin), "current-text", streamNumber, nullptr); + break; + default: + break; + } + } +} + +int QGstreamerPlayerSession::volume() const +{ + return m_volume; +} + +bool QGstreamerPlayerSession::isMuted() const +{ + return m_muted; +} + +bool QGstreamerPlayerSession::isAudioAvailable() const +{ + return m_audioAvailable; +} + +static GstPadProbeReturn block_pad_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) +{ + Q_UNUSED(pad); + Q_UNUSED(info); + Q_UNUSED(user_data); + return GST_PAD_PROBE_OK; +} + +void QGstreamerPlayerSession::updateVideoRenderer() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << "Video sink has chaged, reload video output"; +#endif + + if (m_videoOutput) + setVideoRenderer(m_videoOutput); +} + +void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + if (m_videoOutput != videoOutput) { + if (m_videoOutput) { + disconnect(m_videoOutput, SIGNAL(sinkChanged()), + this, SLOT(updateVideoRenderer())); + disconnect(m_videoOutput, SIGNAL(readyChanged(bool)), + this, SLOT(updateVideoRenderer())); + + m_busHelper->removeMessageFilter(m_videoOutput); + } + + m_videoOutput = videoOutput; + + if (m_videoOutput) { + connect(m_videoOutput, SIGNAL(sinkChanged()), + this, SLOT(updateVideoRenderer())); + connect(m_videoOutput, SIGNAL(readyChanged(bool)), + this, SLOT(updateVideoRenderer())); + + m_busHelper->installMessageFilter(m_videoOutput); + } + } + + m_renderer = qobject_cast(videoOutput); + emit rendererChanged(); + + // No sense to continue if custom pipeline requested. + if (!m_playbin) + return; + + GstElement *videoSink = 0; + if (m_renderer && m_renderer->isReady()) + videoSink = m_renderer->videoSink(); + + if (!videoSink) + videoSink = m_nullVideoSink; + +#ifdef DEBUG_PLAYBIN + qDebug() << "Set video output:" << videoOutput; + qDebug() << "Current sink:" << (m_videoSink ? GST_ELEMENT_NAME(m_videoSink) : "") << m_videoSink + << "pending:" << (m_pendingVideoSink ? GST_ELEMENT_NAME(m_pendingVideoSink) : "") << m_pendingVideoSink + << "new sink:" << (videoSink ? GST_ELEMENT_NAME(videoSink) : "") << videoSink; +#endif + + if (m_pendingVideoSink == videoSink || + (m_pendingVideoSink == 0 && m_videoSink == videoSink)) { +#ifdef DEBUG_PLAYBIN + qDebug() << "Video sink has not changed, skip video output reconfiguration"; +#endif + return; + } + +#ifdef DEBUG_PLAYBIN + qDebug() << "Reconfigure video output"; +#endif + + if (m_state == QMediaPlayer::StoppedState) { +#ifdef DEBUG_PLAYBIN + qDebug() << "The pipeline has not started yet, pending state:" << m_pendingState; +#endif + //the pipeline has not started yet + flushVideoProbes(); + m_pendingVideoSink = 0; + gst_element_set_state(m_videoSink, GST_STATE_NULL); + gst_element_set_state(m_playbin, GST_STATE_NULL); + + removeVideoBufferProbe(); + + gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink); + + m_videoSink = videoSink; + + gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); + + bool linked = gst_element_link(m_videoIdentity, m_videoSink); + if (!linked) + qWarning() << "Linking video output element failed"; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame") != 0) { + gboolean value = m_displayPrerolledFrame; + g_object_set(G_OBJECT(m_videoSink), "show-preroll-frame", value, nullptr); + } + + addVideoBufferProbe(); + + switch (m_pendingState) { + case QMediaPlayer::PausedState: + gst_element_set_state(m_playbin, GST_STATE_PAUSED); + break; + case QMediaPlayer::PlayingState: + gst_element_set_state(m_playbin, GST_STATE_PLAYING); + break; + default: + break; + } + + resumeVideoProbes(); + + } else { + if (m_pendingVideoSink) { +#ifdef DEBUG_PLAYBIN + qDebug() << "already waiting for pad to be blocked, just change the pending sink"; +#endif + m_pendingVideoSink = videoSink; + return; + } + + m_pendingVideoSink = videoSink; + +#ifdef DEBUG_PLAYBIN + qDebug() << "Blocking the video output pad..."; +#endif + + //block pads, async to avoid locking in paused state + GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); + this->pad_probe_id = gst_pad_add_probe(srcPad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BLOCKING), block_pad_cb, this, nullptr); + gst_object_unref(GST_OBJECT(srcPad)); + + //Unpause the sink to avoid waiting until the buffer is processed + //while the sink is paused. The pad will be blocked as soon as the current + //buffer is processed. + if (m_state == QMediaPlayer::PausedState) { +#ifdef DEBUG_PLAYBIN + qDebug() << "Starting video output to avoid blocking in paused state..."; +#endif + gst_element_set_state(m_videoSink, GST_STATE_PLAYING); + } + } +} + +void QGstreamerPlayerSession::finishVideoOutputChange() +{ + if (!m_playbin || !m_pendingVideoSink) + return; + +#ifdef DEBUG_PLAYBIN + qDebug() << "finishVideoOutputChange" << m_pendingVideoSink; +#endif + + GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); + + if (!gst_pad_is_blocked(srcPad)) { + //pad is not blocked, it's possible to swap outputs only in the null state + qWarning() << "Pad is not blocked yet, could not switch video sink"; + GstState identityElementState = GST_STATE_NULL; + gst_element_get_state(m_videoIdentity, &identityElementState, nullptr, GST_CLOCK_TIME_NONE); + if (identityElementState != GST_STATE_NULL) { + gst_object_unref(GST_OBJECT(srcPad)); + return; //can't change vo yet, received async call from the previous change + } + } + + if (m_pendingVideoSink == m_videoSink) { + qDebug() << "Abort, no change"; + //video output was change back to the current one, + //no need to torment the pipeline, just unblock the pad + if (gst_pad_is_blocked(srcPad)) + gst_pad_remove_probe(srcPad, this->pad_probe_id); + + m_pendingVideoSink = 0; + gst_object_unref(GST_OBJECT(srcPad)); + return; + } + + gst_element_set_state(m_videoSink, GST_STATE_NULL); + gst_element_unlink(m_videoIdentity, m_videoSink); + + removeVideoBufferProbe(); + + gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink); + + m_videoSink = m_pendingVideoSink; + m_pendingVideoSink = 0; + + gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); + + addVideoBufferProbe(); + + bool linked = gst_element_link(m_videoIdentity, m_videoSink); + + if (!linked) + qWarning() << "Linking video output element failed"; + +#ifdef DEBUG_PLAYBIN + qDebug() << "notify the video connector it has to emit a new segment message..."; +#endif + + GstState state = GST_STATE_VOID_PENDING; + + switch (m_pendingState) { + case QMediaPlayer::StoppedState: + state = GST_STATE_NULL; + break; + case QMediaPlayer::PausedState: + state = GST_STATE_PAUSED; + break; + case QMediaPlayer::PlayingState: + state = GST_STATE_PLAYING; + break; + } + + gst_element_set_state(m_videoSink, state); + + if (state == GST_STATE_NULL) + flushVideoProbes(); + + // Set state change that was deferred due the video output + // change being pending + gst_element_set_state(m_playbin, state); + + if (state != GST_STATE_NULL) + resumeVideoProbes(); + + //don't have to wait here, it will unblock eventually + if (gst_pad_is_blocked(srcPad)) + gst_pad_remove_probe(srcPad, this->pad_probe_id); + + gst_object_unref(GST_OBJECT(srcPad)); + +} + +bool QGstreamerPlayerSession::isVideoAvailable() const +{ + return m_videoAvailable; +} + +bool QGstreamerPlayerSession::isSeekable() const +{ + return m_seekable; +} + +bool QGstreamerPlayerSession::play() +{ + static bool dumpDot = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR"); + if (dumpDot) + gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_pipeline), GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL), "gst.play"); +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + + m_everPlayed = false; + if (m_pipeline) { + m_pendingState = QMediaPlayer::PlayingState; + if (gst_element_set_state(m_pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + qWarning() << "GStreamer; Unable to play -" << m_request.url().toString(); + m_pendingState = m_state = QMediaPlayer::StoppedState; + emit stateChanged(m_state); + } else { + resumeVideoProbes(); + return true; + } + } + + return false; +} + +bool QGstreamerPlayerSession::pause() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + if (m_pipeline) { + m_pendingState = QMediaPlayer::PausedState; + if (m_pendingVideoSink != 0) + return true; + + if (gst_element_set_state(m_pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { + qWarning() << "GStreamer; Unable to pause -" << m_request.url().toString(); + m_pendingState = m_state = QMediaPlayer::StoppedState; + emit stateChanged(m_state); + } else { + resumeVideoProbes(); + return true; + } + } + + return false; +} + +void QGstreamerPlayerSession::stop() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + m_everPlayed = false; + if (m_pipeline) { + + if (m_renderer) + m_renderer->stopRenderer(); + + flushVideoProbes(); + gst_element_set_state(m_pipeline, GST_STATE_NULL); + + m_lastPosition = 0; + QMediaPlayer::State oldState = m_state; + m_pendingState = m_state = QMediaPlayer::StoppedState; + + finishVideoOutputChange(); + + //we have to do it here, since gstreamer will not emit bus messages any more + setSeekable(false); + if (oldState != m_state) + emit stateChanged(m_state); + } +} + +bool QGstreamerPlayerSession::seek(qint64 ms) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << ms; +#endif + //seek locks when the video output sink is changing and pad is blocked + if (m_pipeline && !m_pendingVideoSink && m_state != QMediaPlayer::StoppedState && m_seekable) { + ms = qMax(ms,qint64(0)); + qint64 from = m_playbackRate > 0 ? ms : 0; + qint64 to = m_playbackRate > 0 ? duration() : ms; + + bool isSeeking = gst_element_seek(m_pipeline, m_playbackRate, GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_FLUSH), + GST_SEEK_TYPE_SET, from * 1000000, + GST_SEEK_TYPE_SET, to * 1000000); + if (isSeeking) + m_lastPosition = ms; + + return isSeeking; + } + + return false; +} + +void QGstreamerPlayerSession::setVolume(int volume) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << volume; +#endif + + if (m_volume != volume) { + m_volume = volume; + + if (m_volumeElement) + g_object_set(G_OBJECT(m_volumeElement), "volume", m_volume / 100.0, nullptr); + + emit volumeChanged(m_volume); + } +} + +void QGstreamerPlayerSession::setMuted(bool muted) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << muted; +#endif + if (m_muted != muted) { + m_muted = muted; + + if (m_volumeElement) + g_object_set(G_OBJECT(m_volumeElement), "mute", m_muted ? TRUE : FALSE, nullptr); + + emit mutedStateChanged(m_muted); + } +} + + +void QGstreamerPlayerSession::setSeekable(bool seekable) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << seekable; +#endif + if (seekable != m_seekable) { + m_seekable = seekable; + emit seekableChanged(m_seekable); + } +} + +bool QGstreamerPlayerSession::processBusMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + if (gm) { + //tag message comes from elements inside playbin, not from playbin itself + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) { + GstTagList *tag_list; + gst_message_parse_tag(gm, &tag_list); + + QMap newTags = QGstUtils::gstTagListToMap(tag_list); + QMap::const_iterator it = newTags.constBegin(); + for ( ; it != newTags.constEnd(); ++it) + m_tags.insert(it.key(), it.value()); // overwrite existing tags + + gst_tag_list_free(tag_list); + + emit tagsChanged(); + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_DURATION) { + updateDuration(); + } + +#ifdef DEBUG_PLAYBIN + if (m_sourceType == MMSSrc && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { + qDebug() << "Message from MMSSrc: " << GST_MESSAGE_TYPE(gm); + } else if (m_sourceType == RTSPSrc && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { + qDebug() << "Message from RTSPSrc: " << GST_MESSAGE_TYPE(gm); + } else { + qDebug() << "Message from " << GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)) << ":" << GST_MESSAGE_TYPE(gm); + } +#endif + + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_BUFFERING) { + int progress = 0; + gst_message_parse_buffering(gm, &progress); + emit bufferingProgressChanged(progress); + } + + bool handlePlaybin2 = false; + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) { + switch (GST_MESSAGE_TYPE(gm)) { + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldState; + GstState newState; + GstState pending; + + gst_message_parse_state_changed(gm, &oldState, &newState, &pending); + +#ifdef DEBUG_PLAYBIN + static QStringList states = { + QStringLiteral("GST_STATE_VOID_PENDING"), QStringLiteral("GST_STATE_NULL"), + QStringLiteral("GST_STATE_READY"), QStringLiteral("GST_STATE_PAUSED"), + QStringLiteral("GST_STATE_PLAYING") }; + + qDebug() << QStringLiteral("state changed: old: %1 new: %2 pending: %3") \ + .arg(states[oldState]) \ + .arg(states[newState]) \ + .arg(states[pending]); +#endif + + switch (newState) { + case GST_STATE_VOID_PENDING: + case GST_STATE_NULL: + setSeekable(false); + finishVideoOutputChange(); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_READY: + setSeekable(false); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_PAUSED: + { + QMediaPlayer::State prevState = m_state; + m_state = QMediaPlayer::PausedState; + + //check for seekable + if (oldState == GST_STATE_READY) { + if (m_sourceType == SoupHTTPSrc || m_sourceType == MMSSrc) { + //since udpsrc is a live source, it is not applicable here + m_everPlayed = true; + } + + getStreamsInfo(); + updateVideoResolutionTag(); + + //gstreamer doesn't give a reliable indication the duration + //information is ready, GST_MESSAGE_DURATION is not sent by most elements + //the duration is queried up to 5 times with increasing delay + m_durationQueries = 5; + // This should also update the seekable flag. + updateDuration(); + + if (!qFuzzyCompare(m_playbackRate, qreal(1.0))) { + qreal rate = m_playbackRate; + m_playbackRate = 1.0; + setPlaybackRate(rate); + } + } + + if (m_state != prevState) + emit stateChanged(m_state); + + break; + } + case GST_STATE_PLAYING: + m_everPlayed = true; + if (m_state != QMediaPlayer::PlayingState) { + emit stateChanged(m_state = QMediaPlayer::PlayingState); + + // For rtsp streams duration information might not be available + // until playback starts. + if (m_duration <= 0) { + m_durationQueries = 5; + updateDuration(); + } + } + + break; + } + } + break; + + case GST_MESSAGE_EOS: + emit playbackFinished(); + break; + + case GST_MESSAGE_TAG: + case GST_MESSAGE_STREAM_STATUS: + case GST_MESSAGE_UNKNOWN: + break; + case GST_MESSAGE_ERROR: { + GError *err; + gchar *debug; + gst_message_parse_error(gm, &err, &debug); + if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND) + processInvalidMedia(QMediaPlayer::FormatError, tr("Cannot play stream of type: ")); + else + processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); + qWarning() << "Error:" << QString::fromUtf8(err->message); + g_error_free(err); + g_free(debug); + } + break; + case GST_MESSAGE_WARNING: + { + GError *err; + gchar *debug; + gst_message_parse_warning (gm, &err, &debug); + qWarning() << "Warning:" << QString::fromUtf8(err->message); + g_error_free (err); + g_free (debug); + } + break; + case GST_MESSAGE_INFO: +#ifdef DEBUG_PLAYBIN + { + GError *err; + gchar *debug; + gst_message_parse_info (gm, &err, &debug); + qDebug() << "Info:" << QString::fromUtf8(err->message); + g_error_free (err); + g_free (debug); + } +#endif + break; + case GST_MESSAGE_BUFFERING: + case GST_MESSAGE_STATE_DIRTY: + case GST_MESSAGE_STEP_DONE: + case GST_MESSAGE_CLOCK_PROVIDE: + case GST_MESSAGE_CLOCK_LOST: + case GST_MESSAGE_NEW_CLOCK: + case GST_MESSAGE_STRUCTURE_CHANGE: + case GST_MESSAGE_APPLICATION: + case GST_MESSAGE_ELEMENT: + break; + case GST_MESSAGE_SEGMENT_START: + { + const GstStructure *structure = gst_message_get_structure(gm); + qint64 position = g_value_get_int64(gst_structure_get_value(structure, "position")); + position /= 1000000; + m_lastPosition = position; + emit positionChanged(position); + } + break; + case GST_MESSAGE_SEGMENT_DONE: + break; + case GST_MESSAGE_LATENCY: + case GST_MESSAGE_ASYNC_START: + break; + case GST_MESSAGE_ASYNC_DONE: + { + gint64 position = 0; + if (qt_gst_element_query_position(m_pipeline, GST_FORMAT_TIME, &position)) { + position /= 1000000; + m_lastPosition = position; + emit positionChanged(position); + } + break; + } + case GST_MESSAGE_REQUEST_STATE: + case GST_MESSAGE_ANY: + break; + default: + break; + } + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { + GError *err; + gchar *debug; + gst_message_parse_error(gm, &err, &debug); + // If the source has given up, so do we. + if (qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { + bool everPlayed = m_everPlayed; + // Try and differentiate network related resource errors from the others + if (!m_request.url().isRelative() && m_request.url().scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) != 0 ) { + if (everPlayed || + (err->domain == GST_RESOURCE_ERROR && ( + err->code == GST_RESOURCE_ERROR_BUSY || + err->code == GST_RESOURCE_ERROR_OPEN_READ || + err->code == GST_RESOURCE_ERROR_READ || + err->code == GST_RESOURCE_ERROR_SEEK || + err->code == GST_RESOURCE_ERROR_SYNC))) { + processInvalidMedia(QMediaPlayer::NetworkError, QString::fromUtf8(err->message)); + } else { + processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); + } + } + else + processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); + } else if (err->domain == GST_STREAM_ERROR + && (err->code == GST_STREAM_ERROR_DECRYPT || err->code == GST_STREAM_ERROR_DECRYPT_NOKEY)) { + processInvalidMedia(QMediaPlayer::AccessDeniedError, QString::fromUtf8(err->message)); + } else { + handlePlaybin2 = true; + } + if (!handlePlaybin2) + qWarning() << "Error:" << QString::fromUtf8(err->message); + g_error_free(err); + g_free(debug); + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT + && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0 + && m_sourceType == UDPSrc + && gst_structure_has_name(gst_message_get_structure(gm), "GstUDPSrcTimeout")) { + //since udpsrc will not generate an error for the timeout event, + //we need to process its element message here and treat it as an error. + processInvalidMedia(m_everPlayed ? QMediaPlayer::NetworkError : QMediaPlayer::ResourceError, + tr("UDP source timeout")); + } else { + handlePlaybin2 = true; + } + + if (handlePlaybin2) { + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_WARNING) { + GError *err; + gchar *debug; + gst_message_parse_warning(gm, &err, &debug); + if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND) + emit error(int(QMediaPlayer::FormatError), tr("Cannot play stream of type: ")); + // GStreamer shows warning for HTTP playlists + if (err && err->message) + qWarning() << "Warning:" << QString::fromUtf8(err->message); + g_error_free(err); + g_free(debug); + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { + GError *err; + gchar *debug; + gst_message_parse_error(gm, &err, &debug); + + // Nearly all errors map to ResourceError + QMediaPlayer::Error qerror = QMediaPlayer::ResourceError; + if (err->domain == GST_STREAM_ERROR + && (err->code == GST_STREAM_ERROR_DECRYPT + || err->code == GST_STREAM_ERROR_DECRYPT_NOKEY)) { + qerror = QMediaPlayer::AccessDeniedError; + } + processInvalidMedia(qerror, QString::fromUtf8(err->message)); + if (err && err->message) + qWarning() << "Error:" << QString::fromUtf8(err->message); + + g_error_free(err); + g_free(debug); + } + } + } + + return false; +} + +void QGstreamerPlayerSession::getStreamsInfo() +{ + if (!m_playbin) + return; + + QList< QMap > oldProperties = m_streamProperties; + QList oldTypes = m_streamTypes; + QMap oldOffset = m_playbin2StreamOffset; + + //check if video is available: + bool haveAudio = false; + bool haveVideo = false; + m_streamProperties.clear(); + m_streamTypes.clear(); + m_playbin2StreamOffset.clear(); + + gint audioStreamsCount = 0; + gint videoStreamsCount = 0; + gint textStreamsCount = 0; + + g_object_get(G_OBJECT(m_playbin), "n-audio", &audioStreamsCount, nullptr); + g_object_get(G_OBJECT(m_playbin), "n-video", &videoStreamsCount, nullptr); + g_object_get(G_OBJECT(m_playbin), "n-text", &textStreamsCount, nullptr); + + haveAudio = audioStreamsCount > 0; + haveVideo = videoStreamsCount > 0; + + m_playbin2StreamOffset[QMediaStreamsControl::AudioStream] = 0; + m_playbin2StreamOffset[QMediaStreamsControl::VideoStream] = audioStreamsCount; + m_playbin2StreamOffset[QMediaStreamsControl::SubPictureStream] = audioStreamsCount+videoStreamsCount; + + for (int i=0; i streamProperties; + + int streamIndex = i - m_playbin2StreamOffset[streamType]; + + GstTagList *tags = 0; + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-audio-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::VideoStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-video-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::SubPictureStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-text-tags", streamIndex, &tags); + break; + default: + break; + } + if (tags && GST_IS_TAG_LIST(tags)) { + gchar *languageCode = 0; + if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) + streamProperties[QMediaMetaData::Language] = QString::fromUtf8(languageCode); + + //qDebug() << "language for setream" << i << QString::fromUtf8(languageCode); + g_free (languageCode); + gst_tag_list_free(tags); + } + + m_streamProperties.append(streamProperties); + } + + bool emitAudioChanged = (haveAudio != m_audioAvailable); + bool emitVideoChanged = (haveVideo != m_videoAvailable); + + m_audioAvailable = haveAudio; + m_videoAvailable = haveVideo; + + if (emitAudioChanged) { + emit audioAvailableChanged(m_audioAvailable); + } + if (emitVideoChanged) { + emit videoAvailableChanged(m_videoAvailable); + } + + if (oldProperties != m_streamProperties || oldTypes != m_streamTypes || oldOffset != m_playbin2StreamOffset) + emit streamsChanged(); +} + +void QGstreamerPlayerSession::updateVideoResolutionTag() +{ + if (!m_videoIdentity) + return; + +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + QSize size; + QSize aspectRatio; + GstPad *pad = gst_element_get_static_pad(m_videoIdentity, "src"); + GstCaps *caps = qt_gst_pad_get_current_caps(pad); + + if (caps) { + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (!size.isEmpty() && gst_structure_get_fraction( + structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + aspectRatio = QSize(aspectNum, aspectDenum); + } + gst_caps_unref(caps); + } + + gst_object_unref(GST_OBJECT(pad)); + + QSize currentSize = m_tags.value("resolution").toSize(); + QSize currentAspectRatio = m_tags.value("pixel-aspect-ratio").toSize(); + + if (currentSize != size || currentAspectRatio != aspectRatio) { + if (aspectRatio.isEmpty()) + m_tags.remove("pixel-aspect-ratio"); + + if (size.isEmpty()) { + m_tags.remove("resolution"); + } else { + m_tags.insert("resolution", QVariant(size)); + if (!aspectRatio.isEmpty()) + m_tags.insert("pixel-aspect-ratio", QVariant(aspectRatio)); + } + + emit tagsChanged(); + } +} + +void QGstreamerPlayerSession::updateDuration() +{ + gint64 gstDuration = 0; + int duration = 0; + + if (m_pipeline && qt_gst_element_query_duration(m_pipeline, GST_FORMAT_TIME, &gstDuration)) + duration = gstDuration / 1000000; + + if (m_duration != duration) { + m_duration = duration; + emit durationChanged(m_duration); + } + + gboolean seekable = false; + if (m_duration > 0) { + m_durationQueries = 0; + GstQuery *query = gst_query_new_seeking(GST_FORMAT_TIME); + if (gst_element_query(m_pipeline, query)) + gst_query_parse_seeking(query, 0, &seekable, 0, 0); + gst_query_unref(query); + } + setSeekable(seekable); + + if (m_durationQueries > 0) { + //increase delay between duration requests + int delay = 25 << (5 - m_durationQueries); + QTimer::singleShot(delay, this, SLOT(updateDuration())); + m_durationQueries--; + } +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << m_duration; +#endif +} + +void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(p); + + GstElement *source = 0; + g_object_get(o, "source", &source, nullptr); + if (source == 0) + return; + +#ifdef DEBUG_PLAYBIN + qDebug() << "Playbin source added:" << G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)); +#endif + + // Set Headers + const QByteArray userAgentString("User-Agent"); + + QGstreamerPlayerSession *self = reinterpret_cast(d); + + // User-Agent - special case, souphhtpsrc will always set something, even if + // defined in extra-headers + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "user-agent") != 0) { + g_object_set(G_OBJECT(source), "user-agent", + self->m_request.rawHeader(userAgentString).constData(), nullptr); + } + + // The rest + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "extra-headers") != 0) { + GstStructure *extras = qt_gst_structure_new_empty("extras"); + + const auto rawHeaderList = self->m_request.rawHeaderList(); + for (const QByteArray &rawHeader : rawHeaderList) { + if (rawHeader == userAgentString) // Filter User-Agent + continue; + + GValue headerValue; + + memset(&headerValue, 0, sizeof(GValue)); + g_value_init(&headerValue, G_TYPE_STRING); + + g_value_set_string(&headerValue, + self->m_request.rawHeader(rawHeader).constData()); + + gst_structure_set_value(extras, rawHeader.constData(), &headerValue); + } + + if (gst_structure_n_fields(extras) > 0) + g_object_set(G_OBJECT(source), "extra-headers", extras, nullptr); + + gst_structure_free(extras); + } + + //set timeout property to 30 seconds + const int timeout = 30; + if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstUDPSrc") == 0) { + quint64 convertedTimeout = timeout; + // Gst 1.x -> nanosecond + convertedTimeout *= 1000000000; + g_object_set(G_OBJECT(source), "timeout", convertedTimeout, nullptr); + self->m_sourceType = UDPSrc; + //The udpsrc is always a live source. + self->m_isLiveSource = true; + + QUrlQuery query(self->m_request.url()); + const QString var = QLatin1String("udpsrc.caps"); + if (query.hasQueryItem(var)) { + GstCaps *caps = gst_caps_from_string(query.queryItemValue(var).toLatin1().constData()); + g_object_set(G_OBJECT(source), "caps", caps, nullptr); + gst_caps_unref(caps); + } + } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstSoupHTTPSrc") == 0) { + //souphttpsrc timeout unit = second + g_object_set(G_OBJECT(source), "timeout", guint(timeout), nullptr); + self->m_sourceType = SoupHTTPSrc; + //since gst_base_src_is_live is not reliable, so we check the source property directly + gboolean isLive = false; + g_object_get(G_OBJECT(source), "is-live", &isLive, nullptr); + self->m_isLiveSource = isLive; + } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstMMSSrc") == 0) { + self->m_sourceType = MMSSrc; + self->m_isLiveSource = gst_base_src_is_live(GST_BASE_SRC(source)); + g_object_set(G_OBJECT(source), "tcp-timeout", G_GUINT64_CONSTANT(timeout*1000000), nullptr); + } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstRTSPSrc") == 0) { + //rtspsrc acts like a live source and will therefore only generate data in the PLAYING state. + self->m_sourceType = RTSPSrc; + self->m_isLiveSource = true; + g_object_set(G_OBJECT(source), "buffer-mode", 1, nullptr); + } else { + self->m_sourceType = UnknownSrc; + self->m_isLiveSource = gst_base_src_is_live(GST_BASE_SRC(source)); + } + +#ifdef DEBUG_PLAYBIN + if (self->m_isLiveSource) + qDebug() << "Current source is a live source"; + else + qDebug() << "Current source is a non-live source"; +#endif + + if (self->m_videoSink) + g_object_set(G_OBJECT(self->m_videoSink), "sync", !self->m_isLiveSource, nullptr); + + gst_object_unref(source); +} + +bool QGstreamerPlayerSession::isLiveSource() const +{ + return m_isLiveSource; +} + +void QGstreamerPlayerSession::handleVolumeChange(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(o); + Q_UNUSED(p); + QGstreamerPlayerSession *session = reinterpret_cast(d); + QMetaObject::invokeMethod(session, "updateVolume", Qt::QueuedConnection); +} + +void QGstreamerPlayerSession::updateVolume() +{ + double volume = 1.0; + g_object_get(m_playbin, "volume", &volume, nullptr); + + if (m_volume != int(volume*100 + 0.5)) { + m_volume = int(volume*100 + 0.5); +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << m_volume; +#endif + emit volumeChanged(m_volume); + } +} + +void QGstreamerPlayerSession::handleMutedChange(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(o); + Q_UNUSED(p); + QGstreamerPlayerSession *session = reinterpret_cast(d); + QMetaObject::invokeMethod(session, "updateMuted", Qt::QueuedConnection); +} + +void QGstreamerPlayerSession::updateMuted() +{ + gboolean muted = FALSE; + g_object_get(G_OBJECT(m_playbin), "mute", &muted, nullptr); + if (m_muted != muted) { + m_muted = muted; +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << m_muted; +#endif + emit mutedStateChanged(muted); + } +} + +GstAutoplugSelectResult QGstreamerPlayerSession::handleAutoplugSelect(GstBin *bin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, QGstreamerPlayerSession *session) +{ + Q_UNUSED(bin); + Q_UNUSED(pad); + Q_UNUSED(caps); + + GstAutoplugSelectResult res = GST_AUTOPLUG_SELECT_TRY; + + // if VAAPI is available and can be used to decode but the current video sink cannot handle + // the decoded format, don't use it + const gchar *factoryName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); + if (g_str_has_prefix(factoryName, "vaapi")) { + GstPad *sinkPad = gst_element_get_static_pad(session->m_videoSink, "sink"); + GstCaps *sinkCaps = gst_pad_query_caps(sinkPad, nullptr); + + if (!gst_element_factory_can_src_any_caps(factory, sinkCaps)) + res = GST_AUTOPLUG_SELECT_SKIP; + + gst_object_unref(sinkPad); + gst_caps_unref(sinkCaps); + } + + return res; +} + +void QGstreamerPlayerSession::handleElementAdded(GstBin *bin, GstElement *element, QGstreamerPlayerSession *session) +{ + Q_UNUSED(bin); + //we have to configure queue2 element to enable media downloading + //and reporting available ranges, + //but it's added dynamically to playbin2 + + gchar *elementName = gst_element_get_name(element); + + if (g_str_has_prefix(elementName, "queue2")) { + // Disable on-disk buffering. + g_object_set(G_OBJECT(element), "temp-template", nullptr, nullptr); + } else if (g_str_has_prefix(elementName, "uridecodebin") || + g_str_has_prefix(elementName, "decodebin")) { + //listen for queue2 element added to uridecodebin/decodebin2 as well. + //Don't touch other bins since they may have unrelated queues + g_signal_connect(element, "element-added", + G_CALLBACK(handleElementAdded), session); + } + + g_free(elementName); +} + +void QGstreamerPlayerSession::handleStreamsChange(GstBin *bin, gpointer user_data) +{ + Q_UNUSED(bin); + + QGstreamerPlayerSession* session = reinterpret_cast(user_data); + QMetaObject::invokeMethod(session, "getStreamsInfo", Qt::QueuedConnection); +} + +//doing proper operations when detecting an invalidMedia: change media status before signal the erorr +void QGstreamerPlayerSession::processInvalidMedia(QMediaPlayer::Error errorCode, const QString& errorString) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + emit invalidMedia(); + stop(); + emit error(int(errorCode), errorString); +} + +void QGstreamerPlayerSession::showPrerollFrames(bool enabled) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << enabled; +#endif + if (enabled != m_displayPrerolledFrame && m_videoSink && + g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame") != 0) { + + gboolean value = enabled; + g_object_set(G_OBJECT(m_videoSink), "show-preroll-frame", value, nullptr); + m_displayPrerolledFrame = enabled; + } +} + +void QGstreamerPlayerSession::addProbe(QGstreamerVideoProbeControl* probe) +{ + Q_ASSERT(!m_videoProbe); + m_videoProbe = probe; + addVideoBufferProbe(); +} + +void QGstreamerPlayerSession::removeProbe(QGstreamerVideoProbeControl* probe) +{ + Q_ASSERT(m_videoProbe == probe); + removeVideoBufferProbe(); + m_videoProbe = 0; +} + +void QGstreamerPlayerSession::addProbe(QGstreamerAudioProbeControl* probe) +{ + Q_ASSERT(!m_audioProbe); + m_audioProbe = probe; + addAudioBufferProbe(); +} + +void QGstreamerPlayerSession::removeProbe(QGstreamerAudioProbeControl* probe) +{ + Q_ASSERT(m_audioProbe == probe); + removeAudioBufferProbe(); + m_audioProbe = 0; +} + +// This function is similar to stop(), +// but does not set m_everPlayed, m_lastPosition, +// and setSeekable() values. +void QGstreamerPlayerSession::endOfMediaReset() +{ + if (m_renderer) + m_renderer->stopRenderer(); + + flushVideoProbes(); + gst_element_set_state(m_pipeline, GST_STATE_PAUSED); + + QMediaPlayer::State oldState = m_state; + m_pendingState = m_state = QMediaPlayer::StoppedState; + + finishVideoOutputChange(); + + if (oldState != m_state) + emit stateChanged(m_state); +} + +void QGstreamerPlayerSession::removeVideoBufferProbe() +{ + if (!m_videoProbe) + return; + + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + if (pad) { + m_videoProbe->removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } +} + +void QGstreamerPlayerSession::addVideoBufferProbe() +{ + if (!m_videoProbe) + return; + + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + if (pad) { + m_videoProbe->addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } +} + +void QGstreamerPlayerSession::removeAudioBufferProbe() +{ + if (!m_audioProbe) + return; + + GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); + if (pad) { + m_audioProbe->removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } +} + +void QGstreamerPlayerSession::addAudioBufferProbe() +{ + if (!m_audioProbe) + return; + + GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); + if (pad) { + m_audioProbe->addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } +} + +void QGstreamerPlayerSession::flushVideoProbes() +{ + if (m_videoProbe) + m_videoProbe->startFlushing(); +} + +void QGstreamerPlayerSession::resumeVideoProbes() +{ + if (m_videoProbe) + m_videoProbe->stopFlushing(); +} + +QT_END_NAMESPACE diff --git a/src/multimedia/gstreamer/qgstreamerplayersession_p.h b/src/multimedia/gstreamer/qgstreamerplayersession_p.h new file mode 100644 index 000000000..f7d09ed3d --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamerplayersession_p.h @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERSESSION_P_H +#define QGSTREAMERPLAYERSESSION_P_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 +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_CONFIG(gstreamer_app) +#include +#endif + +#include + +QT_BEGIN_NAMESPACE + +class QGstreamerBusHelper; +class QGstreamerMessage; + +class QGstreamerVideoRendererInterface; +class QGstreamerVideoProbeControl; +class QGstreamerAudioProbeControl; + +typedef enum { + GST_AUTOPLUG_SELECT_TRY, + GST_AUTOPLUG_SELECT_EXPOSE, + GST_AUTOPLUG_SELECT_SKIP +} GstAutoplugSelectResult; + +class Q_MULTIMEDIA_EXPORT QGstreamerPlayerSession + : public QObject + , public QGstreamerBusMessageFilter +{ +Q_OBJECT +Q_INTERFACES(QGstreamerBusMessageFilter) + +public: + QGstreamerPlayerSession(QObject *parent); + virtual ~QGstreamerPlayerSession(); + + GstElement *playbin() const; + GstElement *pipeline() const { return m_pipeline; } + QGstreamerBusHelper *bus() const { return m_busHelper; } + + QNetworkRequest request() const; + + QMediaPlayer::State state() const { return m_state; } + QMediaPlayer::State pendingState() const { return m_pendingState; } + + qint64 duration() const; + qint64 position() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + + void setVideoRenderer(QObject *renderer); + QGstreamerVideoRendererInterface *renderer() const { return m_renderer; } + bool isVideoAvailable() const; + + bool isSeekable() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMediaTimeRange availablePlaybackRanges() const; + + QMap tags() const { return m_tags; } + QMap streamProperties(int streamNumber) const { return m_streamProperties[streamNumber]; } + int streamCount() const { return m_streamProperties.count(); } + QMediaStreamsControl::StreamType streamType(int streamNumber) { return m_streamTypes.value(streamNumber, QMediaStreamsControl::UnknownStream); } + + int activeStream(QMediaStreamsControl::StreamType streamType) const; + void setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber); + + bool processBusMessage(const QGstreamerMessage &message) override; + +#if QT_CONFIG(gstreamer_app) + QGstAppSrc *appsrc() const { return m_appSrc; } + static void configureAppSrcElement(GObject*, GObject*, GParamSpec*,QGstreamerPlayerSession* _this); +#endif + + bool isLiveSource() const; + + void addProbe(QGstreamerVideoProbeControl* probe); + void removeProbe(QGstreamerVideoProbeControl* probe); + + void addProbe(QGstreamerAudioProbeControl* probe); + void removeProbe(QGstreamerAudioProbeControl* probe); + + void endOfMediaReset(); + +public slots: + void loadFromUri(const QNetworkRequest &url); + void loadFromStream(const QNetworkRequest &url, QIODevice *stream); + bool play(); + bool pause(); + void stop(); + + bool seek(qint64 pos); + + void setVolume(int volume); + void setMuted(bool muted); + + void showPrerollFrames(bool enabled); + +signals: + void durationChanged(qint64 duration); + void positionChanged(qint64 position); + void stateChanged(QMediaPlayer::State state); + void volumeChanged(int volume); + void mutedStateChanged(bool muted); + void audioAvailableChanged(bool audioAvailable); + void videoAvailableChanged(bool videoAvailable); + void bufferingProgressChanged(int percentFilled); + void playbackFinished(); + void tagsChanged(); + void streamsChanged(); + void seekableChanged(bool); + void error(int error, const QString &errorString); + void invalidMedia(); + void playbackRateChanged(qreal); + void rendererChanged(); + void pipelineChanged(); + +private slots: + void getStreamsInfo(); + void setSeekable(bool); + void finishVideoOutputChange(); + void updateVideoRenderer(); + void updateVideoResolutionTag(); + void updateVolume(); + void updateMuted(); + void updateDuration(); + +private: + static void playbinNotifySource(GObject *o, GParamSpec *p, gpointer d); + static void handleVolumeChange(GObject *o, GParamSpec *p, gpointer d); + static void handleMutedChange(GObject *o, GParamSpec *p, gpointer d); + static void handleElementAdded(GstBin *bin, GstElement *element, QGstreamerPlayerSession *session); + static void handleStreamsChange(GstBin *bin, gpointer user_data); + static GstAutoplugSelectResult handleAutoplugSelect(GstBin *bin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, QGstreamerPlayerSession *session); + + void processInvalidMedia(QMediaPlayer::Error errorCode, const QString& errorString); + + void removeVideoBufferProbe(); + void addVideoBufferProbe(); + void removeAudioBufferProbe(); + void addAudioBufferProbe(); + void flushVideoProbes(); + void resumeVideoProbes(); + bool parsePipeline(); + bool setPipeline(GstElement *pipeline); + void resetElements(); + void initPlaybin(); + void setBus(GstBus *bus); + + QNetworkRequest m_request; + QMediaPlayer::State m_state = QMediaPlayer::StoppedState; + QMediaPlayer::State m_pendingState = QMediaPlayer::StoppedState; + QGstreamerBusHelper *m_busHelper = nullptr; + GstElement *m_playbin = nullptr; + GstElement *m_pipeline = nullptr; + + GstElement *m_videoSink = nullptr; + + GstElement *m_videoOutputBin = nullptr; + GstElement *m_videoIdentity = nullptr; + GstElement *m_pendingVideoSink = nullptr; + GstElement *m_nullVideoSink = nullptr; + + GstElement *m_audioSink = nullptr; + GstElement *m_volumeElement = nullptr; + + GstBus *m_bus = nullptr; + QObject *m_videoOutput = nullptr; + QGstreamerVideoRendererInterface *m_renderer = nullptr; + +#if QT_CONFIG(gstreamer_app) + QGstAppSrc *m_appSrc = nullptr; +#endif + + QMap m_tags; + QList< QMap > m_streamProperties; + QList m_streamTypes; + QMap m_playbin2StreamOffset; + + QGstreamerVideoProbeControl *m_videoProbe = nullptr; + QGstreamerAudioProbeControl *m_audioProbe = nullptr; + + int m_volume = 100; + qreal m_playbackRate = 1.0; + bool m_muted = false; + bool m_audioAvailable = false; + bool m_videoAvailable = false; + bool m_seekable = false; + + mutable qint64 m_lastPosition = 0; + qint64 m_duration = 0; + int m_durationQueries = 0; + + bool m_displayPrerolledFrame = true; + + enum SourceType + { + UnknownSrc, + SoupHTTPSrc, + UDPSrc, + MMSSrc, + RTSPSrc, + }; + SourceType m_sourceType = UnknownSrc; + bool m_everPlayed = false; + bool m_isLiveSource = false; + + gulong pad_probe_id = 0; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERPLAYERSESSION_H diff --git a/src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol.cpp b/src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol.cpp new file mode 100644 index 000000000..088b97101 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 "qgstreamervideoinputdevicecontrol_p.h" + +#include +#include + +#include + +QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl(QObject *parent) + : QVideoDeviceSelectorControl(parent) +{ +} + +QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl( + GstElementFactory *factory, QObject *parent) + : QVideoDeviceSelectorControl(parent) + , m_factory(factory) +{ + if (m_factory) + gst_object_ref(GST_OBJECT(m_factory)); +} + +QGstreamerVideoInputDeviceControl::~QGstreamerVideoInputDeviceControl() +{ + if (m_factory) + gst_object_unref(GST_OBJECT(m_factory)); +} + +int QGstreamerVideoInputDeviceControl::deviceCount() const +{ + return QGstUtils::enumerateCameras().count(); +} + +QString QGstreamerVideoInputDeviceControl::deviceName(int index) const +{ + return QGstUtils::enumerateCameras().value(index).name; +} + +QString QGstreamerVideoInputDeviceControl::deviceDescription(int index) const +{ + return QGstUtils::enumerateCameras().value(index).description; +} + +QCamera::Position QGstreamerVideoInputDeviceControl::cameraPosition(int index) const +{ + return QGstUtils::enumerateCameras().value(index).position; +} + +int QGstreamerVideoInputDeviceControl::cameraOrientation(int index) const +{ + return QGstUtils::enumerateCameras().value(index).orientation; +} + +int QGstreamerVideoInputDeviceControl::defaultDevice() const +{ + return 0; +} + +int QGstreamerVideoInputDeviceControl::selectedDevice() const +{ + return m_selectedDevice; +} + +void QGstreamerVideoInputDeviceControl::setSelectedDevice(int index) +{ + // Always update selected device and proxy it to clients + m_selectedDevice = index; + emit selectedDeviceChanged(index); + emit selectedDeviceChanged(deviceName(index)); +} diff --git a/src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol_p.h b/src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol_p.h new file mode 100644 index 000000000..632b6dbb4 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideoinputdevicecontrol_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOINPUTDEVICECONTROL_H +#define QGSTREAMERVIDEOINPUTDEVICECONTROL_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 +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerVideoInputDeviceControl : public QVideoDeviceSelectorControl +{ +Q_OBJECT +public: + QGstreamerVideoInputDeviceControl(QObject *parent); + QGstreamerVideoInputDeviceControl(GstElementFactory *factory, QObject *parent); + ~QGstreamerVideoInputDeviceControl(); + + int deviceCount() const override; + + QString deviceName(int index) const override; + QString deviceDescription(int index) const override; + QCamera::Position cameraPosition(int index) const override; + int cameraOrientation(int index) const override; + + int defaultDevice() const override; + int selectedDevice() const override; + + static QString primaryCamera() { return tr("Main camera"); } + static QString secondaryCamera() { return tr("Front camera"); } + +public Q_SLOTS: + void setSelectedDevice(int index) override; + +private: + GstElementFactory *m_factory = nullptr; + + int m_selectedDevice = 0; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERAUDIOINPUTDEVICECONTROL_H diff --git a/src/multimedia/gstreamer/qgstreamervideooverlay.cpp b/src/multimedia/gstreamer/qgstreamervideooverlay.cpp new file mode 100644 index 000000000..df3229736 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideooverlay.cpp @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** 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 "qgstreamervideooverlay_p.h" + +#include +#include "qgstutils_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +struct ElementMap +{ + const char *qtPlatform; + const char *gstreamerElement; +}; + +// Ordered by descending priority +static constexpr ElementMap elementMap[] = +{ +#if QT_CONFIG(gstreamer_gl) + { "xcb", "glimagesink" }, +#endif + { "xcb", "vaapisink" }, + { "xcb", "xvimagesink" }, + { "xcb", "ximagesink" } +}; + +class QGstreamerSinkProperties +{ +public: + virtual ~QGstreamerSinkProperties() + { + } + + virtual bool hasShowPrerollFrame() const = 0; + virtual void reset() = 0; + virtual int brightness() const = 0; + virtual bool setBrightness(int brightness) = 0; + virtual int contrast() const = 0; + virtual bool setContrast(int contrast) = 0; + virtual int hue() const = 0; + virtual bool setHue(int hue) = 0; + virtual int saturation() const = 0; + virtual bool setSaturation(int saturation) = 0; + virtual Qt::AspectRatioMode aspectRatioMode() const = 0; + virtual void setAspectRatioMode(Qt::AspectRatioMode mode) = 0; +}; + +class QXVImageSinkProperties : public QGstreamerSinkProperties +{ +public: + QXVImageSinkProperties(GstElement *sink) + : m_videoSink(sink) + { + m_hasForceAspectRatio = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "force-aspect-ratio"); + m_hasBrightness = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness"); + m_hasContrast = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast"); + m_hasHue = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue"); + m_hasSaturation = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation"); + m_hasShowPrerollFrame = g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame"); + } + + bool hasShowPrerollFrame() const override + { + return m_hasShowPrerollFrame; + } + + void reset() override + { + setAspectRatioMode(m_aspectRatioMode); + setBrightness(m_brightness); + setContrast(m_contrast); + setHue(m_hue); + setSaturation(m_saturation); + } + + int brightness() const override + { + int brightness = 0; + if (m_hasBrightness) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, nullptr); + + return brightness / 10; + } + + bool setBrightness(int brightness) override + { + m_brightness = brightness; + if (m_hasBrightness) + g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, nullptr); + + return m_hasBrightness; + } + + int contrast() const override + { + int contrast = 0; + if (m_hasContrast) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, nullptr); + + return contrast / 10; + } + + bool setContrast(int contrast) override + { + m_contrast = contrast; + if (m_hasContrast) + g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, nullptr); + + return m_hasContrast; + } + + int hue() const override + { + int hue = 0; + if (m_hasHue) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, nullptr); + + return hue / 10; + } + + bool setHue(int hue) override + { + m_hue = hue; + if (m_hasHue) + g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, nullptr); + + return m_hasHue; + } + + int saturation() const override + { + int saturation = 0; + if (m_hasSaturation) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, nullptr); + + return saturation / 10; + } + + bool setSaturation(int saturation) override + { + m_saturation = saturation; + if (m_hasSaturation) + g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, nullptr); + + return m_hasSaturation; + } + + Qt::AspectRatioMode aspectRatioMode() const override + { + Qt::AspectRatioMode mode = Qt::KeepAspectRatio; + if (m_hasForceAspectRatio) { + gboolean forceAR = false; + g_object_get(G_OBJECT(m_videoSink), "force-aspect-ratio", &forceAR, nullptr); + if (!forceAR) + mode = Qt::IgnoreAspectRatio; + } + + return mode; + } + + void setAspectRatioMode(Qt::AspectRatioMode mode) override + { + m_aspectRatioMode = mode; + if (m_hasForceAspectRatio) { + g_object_set(G_OBJECT(m_videoSink), + "force-aspect-ratio", + (mode == Qt::KeepAspectRatio), + nullptr); + } + } + +protected: + + GstElement *m_videoSink = nullptr; + bool m_hasForceAspectRatio = false; + bool m_hasBrightness = false; + bool m_hasContrast = false; + bool m_hasHue = false; + bool m_hasSaturation = false; + bool m_hasShowPrerollFrame = false; + Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio; + int m_brightness = 0; + int m_contrast = 0; + int m_hue = 0; + int m_saturation = 0; +}; + +class QVaapiSinkProperties : public QXVImageSinkProperties +{ +public: + QVaapiSinkProperties(GstElement *sink) + : QXVImageSinkProperties(sink) + { + // Set default values. + m_contrast = 1; + m_saturation = 1; + } + + int brightness() const override + { + gfloat brightness = 0; + if (m_hasBrightness) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, nullptr); + + return brightness * 100; // [-1,1] -> [-100,100] + } + + bool setBrightness(int brightness) override + { + m_brightness = brightness; + if (m_hasBrightness) { + gfloat v = brightness / 100.0; // [-100,100] -> [-1,1] + g_object_set(G_OBJECT(m_videoSink), "brightness", v, nullptr); + } + + return m_hasBrightness; + } + + int contrast() const override + { + gfloat contrast = 1; + if (m_hasContrast) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, nullptr); + + return (contrast - 1) * 100; // [0,2] -> [-100,100] + } + + bool setContrast(int contrast) override + { + m_contrast = contrast; + if (m_hasContrast) { + gfloat v = (contrast / 100.0) + 1; // [-100,100] -> [0,2] + g_object_set(G_OBJECT(m_videoSink), "contrast", v, nullptr); + } + + return m_hasContrast; + } + + int hue() const override + { + gfloat hue = 0; + if (m_hasHue) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, nullptr); + + return hue / 180 * 100; // [-180,180] -> [-100,100] + } + + bool setHue(int hue) override + { + m_hue = hue; + if (m_hasHue) { + gfloat v = hue / 100.0 * 180; // [-100,100] -> [-180,180] + g_object_set(G_OBJECT(m_videoSink), "hue", v, nullptr); + } + + return m_hasHue; + } + + int saturation() const override + { + gfloat saturation = 1; + if (m_hasSaturation) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, nullptr); + + return (saturation - 1) * 100; // [0,2] -> [-100,100] + } + + bool setSaturation(int saturation) override + { + m_saturation = saturation; + if (m_hasSaturation) { + gfloat v = (saturation / 100.0) + 1; // [-100,100] -> [0,2] + g_object_set(G_OBJECT(m_videoSink), "saturation", v, nullptr); + } + + return m_hasSaturation; + } +}; + +static bool qt_gst_element_is_functioning(GstElement *element) +{ + GstStateChangeReturn ret = gst_element_set_state(element, GST_STATE_READY); + if (ret == GST_STATE_CHANGE_SUCCESS) { + gst_element_set_state(element, GST_STATE_NULL); + return true; + } + + return false; +} + +static GstElement *findBestVideoSink() +{ + GstElement *choice = 0; + QString platform = QGuiApplication::platformName(); + + // We need a native window ID to use the GstVideoOverlay interface. + // Bail out if the Qt platform plugin in use cannot provide a sensible WId. + if (platform != QLatin1String("xcb")) + return 0; + + // First, try some known video sinks, depending on the Qt platform plugin in use. + for (auto i : elementMap) { +#if QT_CONFIG(gstreamer_gl) + if (!QGstUtils::useOpenGL() && qstrcmp(i.gstreamerElement, "glimagesink") == 0) + continue; +#endif + if (platform == QLatin1String(i.qtPlatform) + && (choice = gst_element_factory_make(i.gstreamerElement, nullptr))) { + + if (qt_gst_element_is_functioning(choice)) + return choice; + + gst_object_unref(choice); + choice = 0; + } + } + + // If none of the known video sinks are available, try to find one that implements the + // GstVideoOverlay interface and has autoplugging rank. + GList *list = qt_gst_video_sinks(); + for (GList *item = list; item != nullptr; item = item->next) { + GstElementFactory *f = GST_ELEMENT_FACTORY(item->data); + + if (!gst_element_factory_has_interface(f, QT_GSTREAMER_VIDEOOVERLAY_INTERFACE_NAME)) + continue; + + if (GstElement *el = gst_element_factory_create(f, nullptr)) { + if (qt_gst_element_is_functioning(el)) { + choice = el; + break; + } + + gst_object_unref(el); + } + } + + gst_plugin_feature_list_free(list); + + return choice; +} + +QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent, const QByteArray &elementName) + : QObject(parent) + , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps) +{ + GstElement *sink = nullptr; + if (!elementName.isEmpty()) + sink = gst_element_factory_make(elementName.constData(), nullptr); + else + sink = findBestVideoSink(); + + setVideoSink(sink); +} + +QGstreamerVideoOverlay::~QGstreamerVideoOverlay() +{ + if (m_videoSink) { + delete m_sinkProperties; + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); + gst_object_unref(GST_OBJECT(m_videoSink)); + } +} + +GstElement *QGstreamerVideoOverlay::videoSink() const +{ + return m_videoSink; +} + +void QGstreamerVideoOverlay::setVideoSink(GstElement *sink) +{ + if (!sink) + return; + + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + m_videoSink = sink; + qt_gst_object_ref_sink(GST_OBJECT(m_videoSink)); + + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); + + QString sinkName(QLatin1String(GST_OBJECT_NAME(sink))); + bool isVaapi = sinkName.startsWith(QLatin1String("vaapisink")); + delete m_sinkProperties; + m_sinkProperties = isVaapi ? new QVaapiSinkProperties(sink) : new QXVImageSinkProperties(sink); + + if (m_sinkProperties->hasShowPrerollFrame()) + g_signal_connect(m_videoSink, "notify::show-preroll-frame", + G_CALLBACK(showPrerollFrameChanged), this); +} + +QSize QGstreamerVideoOverlay::nativeVideoSize() const +{ + return m_nativeVideoSize; +} + +void QGstreamerVideoOverlay::setWindowHandle(WId id) +{ + m_windowId = id; + + if (isActive()) + setWindowHandle_helper(id); +} + +void QGstreamerVideoOverlay::setWindowHandle_helper(WId id) +{ + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), id); + + // Properties need to be reset when changing the winId. + m_sinkProperties->reset(); + } +} + +void QGstreamerVideoOverlay::expose() +{ + if (!isActive()) + return; + + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) + gst_video_overlay_expose(GST_VIDEO_OVERLAY(m_videoSink)); +} + +void QGstreamerVideoOverlay::setRenderRectangle(const QRect &rect) +{ + int x = -1; + int y = -1; + int w = -1; + int h = -1; + + if (!rect.isEmpty()) { + x = rect.x(); + y = rect.y(); + w = rect.width(); + h = rect.height(); + } + + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) + gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink), x, y, w, h); +} + +bool QGstreamerVideoOverlay::processSyncMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && + gst_structure_has_name(gst_message_get_structure(gm), "prepare-window-handle")) { + setWindowHandle_helper(m_windowId); + return true; + } + + return false; +} + +bool QGstreamerVideoOverlay::processBusMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED && + GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink)) { + + updateIsActive(); + } + + return false; +} + +void QGstreamerVideoOverlay::probeCaps(GstCaps *caps) +{ + QSize size = QGstUtils::capsCorrectedResolution(caps); + if (size != m_nativeVideoSize) { + m_nativeVideoSize = size; + emit nativeVideoSizeChanged(); + } +} + +bool QGstreamerVideoOverlay::isActive() const +{ + return m_isActive; +} + +void QGstreamerVideoOverlay::updateIsActive() +{ + if (!m_videoSink) + return; + + GstState state = GST_STATE(m_videoSink); + gboolean showPreroll = true; + + if (m_sinkProperties->hasShowPrerollFrame()) + g_object_get(G_OBJECT(m_videoSink), "show-preroll-frame", &showPreroll, nullptr); + + bool newIsActive = (state == GST_STATE_PLAYING || (state == GST_STATE_PAUSED && showPreroll)); + + if (newIsActive != m_isActive) { + m_isActive = newIsActive; + emit activeChanged(); + } +} + +void QGstreamerVideoOverlay::showPrerollFrameChanged(GObject *, GParamSpec *, QGstreamerVideoOverlay *overlay) +{ + overlay->updateIsActive(); +} + +Qt::AspectRatioMode QGstreamerVideoOverlay::aspectRatioMode() const +{ + return m_sinkProperties->aspectRatioMode(); +} + +void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_sinkProperties->setAspectRatioMode(mode); +} + +int QGstreamerVideoOverlay::brightness() const +{ + return m_sinkProperties->brightness(); +} + +void QGstreamerVideoOverlay::setBrightness(int brightness) +{ + if (m_sinkProperties->setBrightness(brightness)) + emit brightnessChanged(brightness); +} + +int QGstreamerVideoOverlay::contrast() const +{ + return m_sinkProperties->contrast(); +} + +void QGstreamerVideoOverlay::setContrast(int contrast) +{ + if (m_sinkProperties->setContrast(contrast)) + emit contrastChanged(contrast); +} + +int QGstreamerVideoOverlay::hue() const +{ + return m_sinkProperties->hue(); +} + +void QGstreamerVideoOverlay::setHue(int hue) +{ + if (m_sinkProperties->setHue(hue)) + emit hueChanged(hue); +} + +int QGstreamerVideoOverlay::saturation() const +{ + return m_sinkProperties->saturation(); +} + +void QGstreamerVideoOverlay::setSaturation(int saturation) +{ + if (m_sinkProperties->setSaturation(saturation)) + emit saturationChanged(saturation); +} + +QT_END_NAMESPACE diff --git a/src/multimedia/gstreamer/qgstreamervideooverlay_p.h b/src/multimedia/gstreamer/qgstreamervideooverlay_p.h new file mode 100644 index 000000000..883da8a4d --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideooverlay_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOVERLAY_P_H +#define QGSTREAMERVIDEOOVERLAY_P_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 +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGstreamerSinkProperties; +class Q_MULTIMEDIA_EXPORT QGstreamerVideoOverlay + : public QObject + , public QGstreamerSyncMessageFilter + , public QGstreamerBusMessageFilter + , private QGstreamerBufferProbe +{ + Q_OBJECT + Q_INTERFACES(QGstreamerSyncMessageFilter QGstreamerBusMessageFilter) +public: + explicit QGstreamerVideoOverlay(QObject *parent = 0, const QByteArray &elementName = QByteArray()); + virtual ~QGstreamerVideoOverlay(); + + GstElement *videoSink() const; + void setVideoSink(GstElement *); + QSize nativeVideoSize() const; + + void setWindowHandle(WId id); + void expose(); + void setRenderRectangle(const QRect &rect); + + bool isActive() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + bool processSyncMessage(const QGstreamerMessage &message) override; + bool processBusMessage(const QGstreamerMessage &message) override; + +Q_SIGNALS: + void nativeVideoSizeChanged(); + void activeChanged(); + void brightnessChanged(int brightness); + void contrastChanged(int contrast); + void hueChanged(int hue); + void saturationChanged(int saturation); + +private: + void setWindowHandle_helper(WId id); + void updateIsActive(); + void probeCaps(GstCaps *caps) override; + static void showPrerollFrameChanged(GObject *, GParamSpec *, QGstreamerVideoOverlay *); + + GstElement *m_videoSink = nullptr; + QSize m_nativeVideoSize; + bool m_isActive = false; + + QGstreamerSinkProperties *m_sinkProperties = nullptr; + WId m_windowId = 0; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERVIDEOOVERLAY_P_H + diff --git a/src/multimedia/gstreamer/qgstreamervideoprobecontrol.cpp b/src/multimedia/gstreamer/qgstreamervideoprobecontrol.cpp new file mode 100644 index 000000000..3d587eb2c --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideoprobecontrol.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** 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 "qgstreamervideoprobecontrol_p.h" + +#include "qgstutils_p.h" +#include + +QGstreamerVideoProbeControl::QGstreamerVideoProbeControl(QObject *parent) + : QMediaVideoProbeControl(parent) +{ +} + +QGstreamerVideoProbeControl::~QGstreamerVideoProbeControl() +{ +} + +void QGstreamerVideoProbeControl::startFlushing() +{ + m_flushing = true; + + { + QMutexLocker locker(&m_frameMutex); + m_pendingFrame = QVideoFrame(); + } + + // only emit flush if at least one frame was probed + if (m_frameProbed) + emit flush(); +} + +void QGstreamerVideoProbeControl::stopFlushing() +{ + m_flushing = false; +} + +void QGstreamerVideoProbeControl::probeCaps(GstCaps *caps) +{ + GstVideoInfo videoInfo; + QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &videoInfo); + + QMutexLocker locker(&m_frameMutex); + m_videoInfo = videoInfo; + m_format = format; +} + +bool QGstreamerVideoProbeControl::probeBuffer(GstBuffer *buffer) +{ + QMutexLocker locker(&m_frameMutex); + + if (m_flushing || !m_format.isValid()) + return true; + + QVideoFrame frame( + new QGstVideoBuffer(buffer, m_videoInfo), + m_format.frameSize(), + m_format.pixelFormat()); + + QGstUtils::setFrameTimeStamps(&frame, buffer); + + m_frameProbed = true; + + if (!m_pendingFrame.isValid()) + QMetaObject::invokeMethod(this, "frameProbed", Qt::QueuedConnection); + m_pendingFrame = frame; + + return true; +} + +void QGstreamerVideoProbeControl::frameProbed() +{ + QVideoFrame frame; + { + QMutexLocker locker(&m_frameMutex); + if (!m_pendingFrame.isValid()) + return; + frame = m_pendingFrame; + m_pendingFrame = QVideoFrame(); + } + emit videoFrameProbed(frame); +} diff --git a/src/multimedia/gstreamer/qgstreamervideoprobecontrol_p.h b/src/multimedia/gstreamer/qgstreamervideoprobecontrol_p.h new file mode 100644 index 000000000..0c4b734b2 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideoprobecontrol_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOPROBECONTROL_H +#define QGSTREAMERVIDEOPROBECONTROL_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 +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerVideoProbeControl + : public QMediaVideoProbeControl + , public QGstreamerBufferProbe + , public QSharedData +{ + Q_OBJECT +public: + explicit QGstreamerVideoProbeControl(QObject *parent); + virtual ~QGstreamerVideoProbeControl(); + + void probeCaps(GstCaps *caps) override; + bool probeBuffer(GstBuffer *buffer) override; + + void startFlushing(); + void stopFlushing(); + +private slots: + void frameProbed(); + +private: + QVideoSurfaceFormat m_format; + QVideoFrame m_pendingFrame; + QMutex m_frameMutex; + GstVideoInfo m_videoInfo; + bool m_flushing = false; + bool m_frameProbed = false; // true if at least one frame was probed +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERVIDEOPROBECONTROL_H diff --git a/src/multimedia/gstreamer/qgstreamervideorenderer.cpp b/src/multimedia/gstreamer/qgstreamervideorenderer.cpp new file mode 100644 index 000000000..c6ca935a4 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideorenderer.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** 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 "qgstreamervideorenderer_p.h" +#include +#include +#include +#include + +#include + +static inline void resetSink(GstElement *&element, GstElement *v = nullptr) +{ + if (element) + gst_object_unref(GST_OBJECT(element)); + + if (v) + qt_gst_object_ref_sink(GST_OBJECT(v)); + + element = v; +} + +QGstreamerVideoRenderer::QGstreamerVideoRenderer(QObject *parent) + : QVideoRendererControl(parent) +{ +} + +QGstreamerVideoRenderer::~QGstreamerVideoRenderer() +{ + resetSink(m_videoSink); +} + +void QGstreamerVideoRenderer::setVideoSink(GstElement *sink) +{ + if (!sink) + return; + + resetSink(m_videoSink, sink); + emit sinkChanged(); +} + +GstElement *QGstreamerVideoRenderer::videoSink() +{ + if (!m_videoSink && m_surface) { + auto sink = reinterpret_cast(QGstVideoRendererSink::createSink(m_surface)); + resetSink(m_videoSink, sink); + } + + return m_videoSink; +} + +void QGstreamerVideoRenderer::stopRenderer() +{ + if (m_surface) + m_surface->stop(); +} + +QAbstractVideoSurface *QGstreamerVideoRenderer::surface() const +{ + return m_surface; +} + +void QGstreamerVideoRenderer::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface != surface) { + resetSink(m_videoSink); + + 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 QGstreamerVideoRenderer::handleFormatChange() +{ + setVideoSink(nullptr); +} diff --git a/src/multimedia/gstreamer/qgstreamervideorenderer_p.h b/src/multimedia/gstreamer/qgstreamervideorenderer_p.h new file mode 100644 index 000000000..10d2c8e2e --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideorenderer_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEORENDERER_H +#define QGSTREAMERVIDEORENDERER_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 +#include +#include +#include + +#include "qgstreamervideorendererinterface_p.h" + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerVideoRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoRenderer(QObject *parent = 0); + virtual ~QGstreamerVideoRenderer(); + + QAbstractVideoSurface *surface() const override; + void setSurface(QAbstractVideoSurface *surface) override; + + GstElement *videoSink() override; + void setVideoSink(GstElement *) override; + + void stopRenderer() override; + bool isReady() const override { return m_surface != 0; } + +signals: + void sinkChanged(); + void readyChanged(bool); + +private slots: + void handleFormatChange(); + +private: + GstElement *m_videoSink = nullptr; + QPointer m_surface; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERVIDEORENDRER_H diff --git a/src/multimedia/gstreamer/qgstreamervideorendererinterface.cpp b/src/multimedia/gstreamer/qgstreamervideorendererinterface.cpp new file mode 100644 index 000000000..ae7de06f1 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideorendererinterface.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** 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 "qgstreamervideorendererinterface_p.h" + +QGstreamerVideoRendererInterface::~QGstreamerVideoRendererInterface() +{ +} diff --git a/src/multimedia/gstreamer/qgstreamervideorendererinterface_p.h b/src/multimedia/gstreamer/qgstreamervideorendererinterface_p.h new file mode 100644 index 000000000..af163c1b5 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideorendererinterface_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOUTPUTCONTROL_H +#define QGSTREAMERVIDEOOUTPUTCONTROL_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 +#include +#include + + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerVideoRendererInterface +{ +public: + virtual ~QGstreamerVideoRendererInterface(); + virtual GstElement *videoSink() = 0; + virtual void setVideoSink(GstElement *) {}; + + //stopRenderer() is called when the renderer element is stopped. + //it can be reimplemented when video renderer can't detect + //changes to NULL state but has to free video resources. + virtual void stopRenderer() {} + + //the video output is configured, usually after the first paint event + //(winId is known, + virtual bool isReady() const { return true; } + + //signals: + //void sinkChanged(); + //void readyChanged(bool); +}; + +#define QGstreamerVideoRendererInterface_iid "org.qt-project.qt.gstreamervideorenderer/5.0" +Q_DECLARE_INTERFACE(QGstreamerVideoRendererInterface, QGstreamerVideoRendererInterface_iid) +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstreamervideowindow.cpp b/src/multimedia/gstreamer/qgstreamervideowindow.cpp new file mode 100644 index 000000000..e7e3c5044 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideowindow.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** 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 "qgstreamervideowindow_p.h" +#include + +#include + +QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const QByteArray &elementName) + : QVideoWindowControl(parent) + , m_videoOverlay(this, !elementName.isEmpty() ? elementName : qgetenv("QT_GSTREAMER_WINDOW_VIDEOSINK")) +{ + connect(&m_videoOverlay, &QGstreamerVideoOverlay::nativeVideoSizeChanged, + this, &QGstreamerVideoWindow::nativeSizeChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::brightnessChanged, + this, &QGstreamerVideoWindow::brightnessChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::contrastChanged, + this, &QGstreamerVideoWindow::contrastChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::hueChanged, + this, &QGstreamerVideoWindow::hueChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::saturationChanged, + this, &QGstreamerVideoWindow::saturationChanged); +} + +QGstreamerVideoWindow::~QGstreamerVideoWindow() +{ +} + +GstElement *QGstreamerVideoWindow::videoSink() +{ + return m_videoOverlay.videoSink(); +} + +WId QGstreamerVideoWindow::winId() const +{ + return m_windowId; +} + +void QGstreamerVideoWindow::setWinId(WId id) +{ + if (m_windowId == id) + return; + + WId oldId = m_windowId; + m_videoOverlay.setWindowHandle(m_windowId = id); + + if (!oldId) + emit readyChanged(true); + + if (!id) + emit readyChanged(false); +} + +bool QGstreamerVideoWindow::processSyncMessage(const QGstreamerMessage &message) +{ + return m_videoOverlay.processSyncMessage(message); +} + +bool QGstreamerVideoWindow::processBusMessage(const QGstreamerMessage &message) +{ + return m_videoOverlay.processBusMessage(message); +} + +QRect QGstreamerVideoWindow::displayRect() const +{ + return m_displayRect; +} + +void QGstreamerVideoWindow::setDisplayRect(const QRect &rect) +{ + m_videoOverlay.setRenderRectangle(m_displayRect = rect); + repaint(); +} + +Qt::AspectRatioMode QGstreamerVideoWindow::aspectRatioMode() const +{ + return m_videoOverlay.aspectRatioMode(); +} + +void QGstreamerVideoWindow::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_videoOverlay.setAspectRatioMode(mode); +} + +void QGstreamerVideoWindow::repaint() +{ + m_videoOverlay.expose(); +} + +int QGstreamerVideoWindow::brightness() const +{ + return m_videoOverlay.brightness(); +} + +void QGstreamerVideoWindow::setBrightness(int brightness) +{ + m_videoOverlay.setBrightness(brightness); +} + +int QGstreamerVideoWindow::contrast() const +{ + return m_videoOverlay.contrast(); +} + +void QGstreamerVideoWindow::setContrast(int contrast) +{ + m_videoOverlay.setContrast(contrast); +} + +int QGstreamerVideoWindow::hue() const +{ + return m_videoOverlay.hue(); +} + +void QGstreamerVideoWindow::setHue(int hue) +{ + m_videoOverlay.setHue(hue); +} + +int QGstreamerVideoWindow::saturation() const +{ + return m_videoOverlay.saturation(); +} + +void QGstreamerVideoWindow::setSaturation(int saturation) +{ + m_videoOverlay.setSaturation(saturation); +} + +bool QGstreamerVideoWindow::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoWindow::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +QSize QGstreamerVideoWindow::nativeSize() const +{ + return m_videoOverlay.nativeVideoSize(); +} diff --git a/src/multimedia/gstreamer/qgstreamervideowindow_p.h b/src/multimedia/gstreamer/qgstreamervideowindow_p.h new file mode 100644 index 000000000..cae656347 --- /dev/null +++ b/src/multimedia/gstreamer/qgstreamervideowindow_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOWINDOW_H +#define QGSTREAMERVIDEOWINDOW_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 +#include + +#include "qgstreamervideorendererinterface_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QAbstractVideoSurface; + +class Q_MULTIMEDIA_EXPORT QGstreamerVideoWindow : + public QVideoWindowControl, + public QGstreamerVideoRendererInterface, + public QGstreamerSyncMessageFilter, + public QGstreamerBusMessageFilter +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter QGstreamerBusMessageFilter) +public: + explicit QGstreamerVideoWindow(QObject *parent = 0, const QByteArray &elementName = QByteArray()); + ~QGstreamerVideoWindow(); + + WId winId() const override; + void setWinId(WId id) override; + + QRect displayRect() const override; + void setDisplayRect(const QRect &rect) override; + + bool isFullScreen() const override; + void setFullScreen(bool fullScreen) override; + + QSize nativeSize() const override; + + Qt::AspectRatioMode aspectRatioMode() const override; + void setAspectRatioMode(Qt::AspectRatioMode mode) override; + + void repaint() override; + + int brightness() const override; + void setBrightness(int brightness) override; + + int contrast() const override; + void setContrast(int contrast) override; + + int hue() const override; + void setHue(int hue) override; + + int saturation() const override; + void setSaturation(int saturation) override; + + QAbstractVideoSurface *surface() const; + + GstElement *videoSink() override; + + bool processSyncMessage(const QGstreamerMessage &message) override; + bool processBusMessage(const QGstreamerMessage &message) override; + bool isReady() const override { return m_windowId != 0; } + +signals: + void sinkChanged(); + void readyChanged(bool); + +private: + QGstreamerVideoOverlay m_videoOverlay; + WId m_windowId = 0; + QRect m_displayRect; + bool m_fullScreen = false; + mutable QColor m_colorKey = QColor::Invalid; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstutils.cpp b/src/multimedia/gstreamer/qgstutils.cpp new file mode 100644 index 000000000..8891aad87 --- /dev/null +++ b/src/multimedia/gstreamer/qgstutils.cpp @@ -0,0 +1,1150 @@ +/**************************************************************************** +** +** 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 +#include "qgstutils_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +template static int lengthOf(const T (&)[N]) { return N; } + +#include "qgstreamervideoinputdevicecontrol_p.h" + +QT_BEGIN_NAMESPACE + +//internal +static void addTagToMap(const GstTagList *list, + const gchar *tag, + gpointer user_data) +{ + QMap *map = reinterpret_cast* >(user_data); + + GValue val; + val.g_type = 0; + gst_tag_list_copy_value(&val,list,tag); + + switch( G_VALUE_TYPE(&val) ) { + case G_TYPE_STRING: + { + const gchar *str_value = g_value_get_string(&val); + map->insert(QByteArray(tag), QString::fromUtf8(str_value)); + break; + } + case G_TYPE_INT: + map->insert(QByteArray(tag), g_value_get_int(&val)); + break; + case G_TYPE_UINT: + map->insert(QByteArray(tag), g_value_get_uint(&val)); + break; + case G_TYPE_LONG: + map->insert(QByteArray(tag), qint64(g_value_get_long(&val))); + break; + case G_TYPE_BOOLEAN: + map->insert(QByteArray(tag), g_value_get_boolean(&val)); + break; + case G_TYPE_CHAR: +#if GLIB_CHECK_VERSION(2,32,0) + map->insert(QByteArray(tag), g_value_get_schar(&val)); +#else + map->insert(QByteArray(tag), g_value_get_char(&val)); +#endif + break; + case G_TYPE_DOUBLE: + map->insert(QByteArray(tag), g_value_get_double(&val)); + break; + default: + // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch + if (G_VALUE_TYPE(&val) == G_TYPE_DATE) { + const GDate *date = (const GDate *)g_value_get_boxed(&val); + if (g_date_valid(date)) { + int year = g_date_get_year(date); + int month = g_date_get_month(date); + int day = g_date_get_day(date); + map->insert(QByteArray(tag), QDate(year,month,day)); + if (!map->contains("year")) + map->insert("year", year); + } + } else if (G_VALUE_TYPE(&val) == GST_TYPE_DATE_TIME) { + const GstDateTime *dateTime = (const GstDateTime *)g_value_get_boxed(&val); + int year = gst_date_time_has_year(dateTime) ? gst_date_time_get_year(dateTime) : 0; + int month = gst_date_time_has_month(dateTime) ? gst_date_time_get_month(dateTime) : 0; + int day = gst_date_time_has_day(dateTime) ? gst_date_time_get_day(dateTime) : 0; + if (gst_date_time_has_time(dateTime)) { + int hour = gst_date_time_get_hour(dateTime); + int minute = gst_date_time_get_minute(dateTime); + int second = gst_date_time_get_second(dateTime); + float tz = gst_date_time_get_time_zone_offset(dateTime); + QDateTime dateTime(QDate(year, month, day), QTime(hour, minute, second), + Qt::OffsetFromUTC, tz * 60 * 60); + map->insert(QByteArray(tag), dateTime); + } else if (year > 0 && month > 0 && day > 0) { + map->insert(QByteArray(tag), QDate(year,month,day)); + } + if (!map->contains("year") && year > 0) + map->insert("year", year); + } else if (G_VALUE_TYPE(&val) == GST_TYPE_SAMPLE) { + GstSample *sample = (GstSample *)g_value_get_boxed(&val); + GstCaps* caps = gst_sample_get_caps(sample); + if (caps && !gst_caps_is_empty(caps)) { + GstStructure *structure = gst_caps_get_structure(caps, 0); + const gchar *name = gst_structure_get_name(structure); + if (QByteArray(name).startsWith("image/")) { + GstBuffer *buffer = gst_sample_get_buffer(sample); + if (buffer) { + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_READ); + map->insert(QByteArray(tag), QImage::fromData(info.data, info.size, name)); + gst_buffer_unmap(buffer, &info); + } + } + } + } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) { + int nom = gst_value_get_fraction_numerator(&val); + int denom = gst_value_get_fraction_denominator(&val); + + if (denom > 0) { + map->insert(QByteArray(tag), double(nom)/denom); + } + } + break; + } + + g_value_unset(&val); +} + +/*! + \class QGstUtils + \internal +*/ + +/*! + Convert GstTagList structure to QMap. + + Mapping to int, bool, char, string, fractions and date are supported. + Fraction values are converted to doubles. +*/ +QMap QGstUtils::gstTagListToMap(const GstTagList *tags) +{ + QMap res; + gst_tag_list_foreach(tags, addTagToMap, &res); + + return res; +} + +/*! + Returns resolution of \a caps. + If caps doesn't have a valid size, an empty QSize is returned. +*/ +QSize QGstUtils::capsResolution(const GstCaps *caps) +{ + if (gst_caps_get_size(caps) == 0) + return QSize(); + + return structureResolution(gst_caps_get_structure(caps, 0)); +} + +/*! + Returns aspect ratio corrected resolution of \a caps. + If caps doesn't have a valid size, an empty QSize is returned. +*/ +QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) +{ + QSize size; + + if (caps) { + size = capsResolution(caps); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (!size.isEmpty() && gst_structure_get_fraction( + gst_caps_get_structure(caps, 0), "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + size.setWidth(size.width()*aspectNum/aspectDenum); + } + } + + return size; +} + + +namespace { + +struct AudioFormat +{ + GstAudioFormat format; + QAudioFormat::SampleType sampleType; + QAudioFormat::Endian byteOrder; + int sampleSize; +}; +static const AudioFormat qt_audioLookup[] = +{ + { GST_AUDIO_FORMAT_S8 , QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 8 }, + { GST_AUDIO_FORMAT_U8 , QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 8 }, + { GST_AUDIO_FORMAT_S16LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 16 }, + { GST_AUDIO_FORMAT_S16BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 16 }, + { GST_AUDIO_FORMAT_U16LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 16 }, + { GST_AUDIO_FORMAT_U16BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 16 }, + { GST_AUDIO_FORMAT_S32LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 32 }, + { GST_AUDIO_FORMAT_S32BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 32 }, + { GST_AUDIO_FORMAT_U32LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 32 }, + { GST_AUDIO_FORMAT_U32BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 32 }, + { GST_AUDIO_FORMAT_S24LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 24 }, + { GST_AUDIO_FORMAT_S24BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 24 }, + { GST_AUDIO_FORMAT_U24LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 24 }, + { GST_AUDIO_FORMAT_U24BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 24 }, + { GST_AUDIO_FORMAT_F32LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 32 }, + { GST_AUDIO_FORMAT_F32BE, QAudioFormat::Float , QAudioFormat::BigEndian , 32 }, + { GST_AUDIO_FORMAT_F64LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 64 }, + { GST_AUDIO_FORMAT_F64BE, QAudioFormat::Float , QAudioFormat::BigEndian , 64 } +}; + +} + +/*! + Returns audio format for caps. + If caps doesn't have a valid audio format, an empty QAudioFormat is returned. +*/ + +QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps) +{ + QAudioFormat format; + GstAudioInfo info; + if (gst_audio_info_from_caps(&info, caps)) { + for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { + if (qt_audioLookup[i].format != info.finfo->format) + continue; + + format.setSampleType(qt_audioLookup[i].sampleType); + format.setByteOrder(qt_audioLookup[i].byteOrder); + format.setSampleSize(qt_audioLookup[i].sampleSize); + format.setSampleRate(info.rate); + format.setChannelCount(info.channels); + format.setCodec(QStringLiteral("audio/pcm")); + + return format; + } + } + + return format; +} + +/* + Returns audio format for a sample. + If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. +*/ +QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample) +{ + GstCaps* caps = gst_sample_get_caps(sample); + if (!caps) + return QAudioFormat(); + + return QGstUtils::audioFormatForCaps(caps); +} + +/*! + Builds GstCaps for an audio format. + Returns 0 if the audio format is not valid. + Caller must unref GstCaps. +*/ + +GstCaps *QGstUtils::capsForAudioFormat(const QAudioFormat &format) +{ + if (!format.isValid()) + return 0; + + const QAudioFormat::SampleType sampleType = format.sampleType(); + const QAudioFormat::Endian byteOrder = format.byteOrder(); + const int sampleSize = format.sampleSize(); + + for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { + if (qt_audioLookup[i].sampleType != sampleType + || qt_audioLookup[i].byteOrder != byteOrder + || qt_audioLookup[i].sampleSize != sampleSize) { + continue; + } + + return gst_caps_new_simple( + "audio/x-raw", + "format" , G_TYPE_STRING, gst_audio_format_to_string(qt_audioLookup[i].format), + "rate" , G_TYPE_INT , format.sampleRate(), + "channels", G_TYPE_INT , format.channelCount(), + nullptr); + } + return 0; +} + +static QSet m_videoSources; +static QSet m_audioSources; +static QSet m_audioSinks; + +static void addDevice(GstDevice *device) +{ + gchar *type = gst_device_get_device_class(device); +// qDebug() << "adding device:" << device << type << gst_device_get_display_name(device); + gst_object_ref(device); + if (!strcmp(type, "Video/Source")) + m_videoSources.insert(device); + else if (!strcmp(type, "Audio/Source")) + m_audioSources.insert(device); + else if (!strcmp(type, "Audio/Sink")) + m_audioSinks.insert(device); + else + gst_object_unref(device); + g_free(type); +} + +static void removeDevice(GstDevice *device) +{ +// qDebug() << "removing device:" << device << gst_device_get_display_name(device); + if (m_videoSources.remove(device) || + m_audioSources.remove(device) || + m_audioSinks.remove(device)) + gst_object_unref(device); +} + +static gboolean deviceMonitor(GstBus *, GstMessage *message, gpointer) +{ + GstDevice *device = nullptr; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_DEVICE_ADDED: + gst_message_parse_device_added (message, &device); + addDevice(device); + break; + case GST_MESSAGE_DEVICE_REMOVED: + gst_message_parse_device_removed (message, &device); + removeDevice(device); + break; + default: + break; + } + if (device) + gst_object_unref (device); + + return G_SOURCE_CONTINUE; +} + +void setupDeviceMonitor() +{ + GstDeviceMonitor *monitor; + GstBus *bus; + + monitor = gst_device_monitor_new(); + + bus = gst_device_monitor_get_bus(monitor); + gst_bus_add_watch(bus, deviceMonitor, NULL); + gst_object_unref(bus); + + gst_device_monitor_add_filter (monitor, "Video/Source", NULL); + gst_device_monitor_add_filter (monitor, "Audio/Source", NULL); + gst_device_monitor_add_filter (monitor, "Audio/Sink", NULL); + + auto devices = gst_device_monitor_get_devices(monitor); + + while (devices) { + GstDevice *device = static_cast(devices->data); + addDevice(device); + gst_object_unref(device); + devices = g_list_delete_link(devices, devices); + } + + gst_device_monitor_start(monitor); +} + + +void QGstUtils::initializeGst() +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + gst_init(nullptr, nullptr); + setupDeviceMonitor(); + } +} + +namespace { + const char* getCodecAlias(const QString &codec) + { + if (codec.startsWith(QLatin1String("avc1."))) + return "video/x-h264"; + + if (codec.startsWith(QLatin1String("mp4a."))) + return "audio/mpeg4"; + + if (codec.startsWith(QLatin1String("mp4v.20."))) + return "video/mpeg4"; + + if (codec == QLatin1String("samr")) + return "audio/amr"; + + return 0; + } + + const char* getMimeTypeAlias(const QString &mimeType) + { + if (mimeType == QLatin1String("video/mp4")) + return "video/mpeg4"; + + if (mimeType == QLatin1String("audio/mp4")) + return "audio/mpeg4"; + + if (mimeType == QLatin1String("video/ogg") + || mimeType == QLatin1String("audio/ogg")) + return "application/ogg"; + + return 0; + } +} + +QMultimedia::SupportEstimate QGstUtils::hasSupport(const QString &mimeType, + const QStringList &codecs, + const QSet &supportedMimeTypeSet) +{ + if (supportedMimeTypeSet.isEmpty()) + return QMultimedia::NotSupported; + + QString mimeTypeLowcase = mimeType.toLower(); + bool containsMimeType = supportedMimeTypeSet.contains(mimeTypeLowcase); + if (!containsMimeType) { + const char* mimeTypeAlias = getMimeTypeAlias(mimeTypeLowcase); + containsMimeType = supportedMimeTypeSet.contains(QLatin1String(mimeTypeAlias)); + if (!containsMimeType) { + containsMimeType = supportedMimeTypeSet.contains(QLatin1String("video/") + mimeTypeLowcase) + || supportedMimeTypeSet.contains(QLatin1String("video/x-") + mimeTypeLowcase) + || supportedMimeTypeSet.contains(QLatin1String("audio/") + mimeTypeLowcase) + || supportedMimeTypeSet.contains(QLatin1String("audio/x-") + mimeTypeLowcase); + } + } + + int supportedCodecCount = 0; + for (const QString &codec : codecs) { + QString codecLowcase = codec.toLower(); + const char* codecAlias = getCodecAlias(codecLowcase); + if (codecAlias) { + if (supportedMimeTypeSet.contains(QLatin1String(codecAlias))) + supportedCodecCount++; + } else if (supportedMimeTypeSet.contains(QLatin1String("video/") + codecLowcase) + || supportedMimeTypeSet.contains(QLatin1String("video/x-") + codecLowcase) + || supportedMimeTypeSet.contains(QLatin1String("audio/") + codecLowcase) + || supportedMimeTypeSet.contains(QLatin1String("audio/x-") + codecLowcase)) { + supportedCodecCount++; + } + } + if (supportedCodecCount > 0 && supportedCodecCount == codecs.size()) + return QMultimedia::ProbablySupported; + + if (supportedCodecCount == 0 && !containsMimeType) + return QMultimedia::NotSupported; + + return QMultimedia::MaybeSupported; +} + +QList QGstUtils::enumerateCameras() +{ + initializeGst(); + + QList devices; + + for (auto *d : qAsConst(m_videoSources)) { + auto *properties = gst_device_get_properties(d); + if (properties) { + CameraInfo info; + auto *desc = gst_device_get_display_name(d); + info.description = QString::fromUtf8(desc); + g_free(desc); + + auto *name = gst_structure_get_string(properties, "device.path"); + info.name = QString::fromUtf8(name); + info.driver = gst_structure_get_string(properties, "v4l2.device.driver"); + info.orientation = 0; + info.position = QCamera::UnspecifiedPosition; + gst_structure_free(properties); + + devices.append(info); + } + } + return devices; +} + +QList QGstUtils::cameraDevices() +{ + QList devices; + + const auto cameras = enumerateCameras(); + devices.reserve(cameras.size()); + for (const CameraInfo &camera : cameras) + devices.append(camera.name.toUtf8()); + + return devices; +} + +QString QGstUtils::cameraDescription(const QString &device) +{ + const auto cameras = enumerateCameras(); + for (const CameraInfo &camera : cameras) { + if (camera.name == device) + return camera.description; + } + return QString(); +} + +QCamera::Position QGstUtils::cameraPosition(const QString &device) +{ + const auto cameras = enumerateCameras(); + for (const CameraInfo &camera : cameras) { + if (camera.name == device) + return camera.position; + } + return QCamera::UnspecifiedPosition; +} + +int QGstUtils::cameraOrientation(const QString &device) +{ + const auto cameras = enumerateCameras(); + for (const CameraInfo &camera : cameras) { + if (camera.name == device) + return camera.orientation; + } + return 0; +} + +QByteArray QGstUtils::cameraDriver(const QString &device) +{ + const auto cameras = enumerateCameras(); + for (const CameraInfo &camera : cameras) { + if (camera.name == device) + return camera.driver; + } + return QByteArray(); +} + + +const QSet &QGstUtils::audioSources() +{ + return m_audioSources; +} + +const QSet &QGstUtils::audioSinks() +{ + return m_audioSinks; +} + +QSet QGstUtils::supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)) +{ + QSet supportedMimeTypes; + + //enumerate supported mime types + gst_init(nullptr, nullptr); + + GstRegistry *registry = gst_registry_get(); + GList *orig_plugins = gst_registry_get_plugin_list(registry); + for (GList *plugins = orig_plugins; plugins; plugins = g_list_next(plugins)) { + GstPlugin *plugin = (GstPlugin *) (plugins->data); + if (GST_OBJECT_FLAG_IS_SET(GST_OBJECT(plugin), GST_PLUGIN_FLAG_BLACKLISTED)) + continue; + + GList *orig_features = gst_registry_get_feature_list_by_plugin( + registry, gst_plugin_get_name(plugin)); + for (GList *features = orig_features; features; features = g_list_next(features)) { + if (G_UNLIKELY(features->data == nullptr)) + continue; + + GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); + GstElementFactory *factory; + + if (GST_IS_TYPE_FIND_FACTORY(feature)) { + QString name(QLatin1String(gst_plugin_feature_get_name(feature))); + if (name.contains(QLatin1Char('/'))) //filter out any string without '/' which is obviously not a mime type + supportedMimeTypes.insert(name.toLower()); + continue; + } else if (!GST_IS_ELEMENT_FACTORY (feature) + || !(factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)))) { + continue; + } else if (!isValidFactory(factory)) { + // Do nothing + } else for (const GList *pads = gst_element_factory_get_static_pad_templates(factory); + pads; + pads = g_list_next(pads)) { + GstStaticPadTemplate *padtemplate = static_cast(pads->data); + + if (padtemplate->direction == GST_PAD_SINK && padtemplate->static_caps.string) { + GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); + if (gst_caps_is_any(caps) || gst_caps_is_empty(caps)) { + } else for (guint i = 0; i < gst_caps_get_size(caps); i++) { + GstStructure *structure = gst_caps_get_structure(caps, i); + QString nameLowcase = QString::fromLatin1(gst_structure_get_name(structure)).toLower(); + + supportedMimeTypes.insert(nameLowcase); + if (nameLowcase.contains(QLatin1String("mpeg"))) { + //Because mpeg version number is only included in the detail + //description, it is necessary to manually extract this information + //in order to match the mime type of mpeg4. + const GValue *value = gst_structure_get_value(structure, "mpegversion"); + if (value) { + gchar *str = gst_value_serialize(value); + QString versions = QLatin1String(str); + const QStringList elements = versions.split(QRegularExpression(QLatin1String("\\D+")), Qt::SkipEmptyParts); + for (const QString &e : elements) + supportedMimeTypes.insert(nameLowcase + e); + g_free(str); + } + } + } + } + } + gst_object_unref(factory); + } + gst_plugin_feature_list_free(orig_features); + } + gst_plugin_list_free (orig_plugins); + +#if defined QT_SUPPORTEDMIMETYPES_DEBUG + QStringList list = supportedMimeTypes.toList(); + list.sort(); + if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { + for (const QString &type : qAsConst(list)) + qDebug() << type; + } +#endif + return supportedMimeTypes; +} + +namespace { + +struct ColorFormat { QImage::Format imageFormat; GstVideoFormat gstFormat; }; +static const ColorFormat qt_colorLookup[] = +{ + { QImage::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx }, + { QImage::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA }, + { QImage::Format_RGB888 , GST_VIDEO_FORMAT_RGB }, + { QImage::Format_RGB16 , GST_VIDEO_FORMAT_RGB16 } +}; + +} + +QImage QGstUtils::bufferToImage(GstBuffer *buffer, const GstVideoInfo &videoInfo) +{ + QImage img; + + GstVideoInfo info = videoInfo; + GstVideoFrame frame; + if (!gst_video_frame_map(&frame, &info, buffer, GST_MAP_READ)) + return img; + + if (videoInfo.finfo->format == GST_VIDEO_FORMAT_I420) { + const int width = videoInfo.width; + const int height = videoInfo.height; + + const int stride[] = { frame.info.stride[0], frame.info.stride[1], frame.info.stride[2] }; + const uchar *data[] = { + static_cast(frame.data[0]), + static_cast(frame.data[1]), + static_cast(frame.data[2]) + }; + img = QImage(width/2, height/2, QImage::Format_RGB32); + + for (int y=0; yformat) + continue; + + const QImage image( + static_cast(frame.data[0]), + videoInfo.width, + videoInfo.height, + frame.info.stride[0], + qt_colorLookup[i].imageFormat); + img = image; + img.detach(); + + break; + } + + gst_video_frame_unmap(&frame); + + return img; +} + + +namespace { + +struct VideoFormat +{ + QVideoFrame::PixelFormat pixelFormat; + GstVideoFormat gstFormat; +}; + +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 }, + { QVideoFrame::Format_NV12 , GST_VIDEO_FORMAT_NV12 }, + { QVideoFrame::Format_NV21 , GST_VIDEO_FORMAT_NV21 }, + { QVideoFrame::Format_AYUV444, GST_VIDEO_FORMAT_AYUV }, +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + { 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 }, + { QVideoFrame::Format_BGR24 , GST_VIDEO_FORMAT_BGR }, + { QVideoFrame::Format_RGB565, GST_VIDEO_FORMAT_RGB16 } +}; + +static int indexOfVideoFormat(QVideoFrame::PixelFormat format) +{ + for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) + if (qt_videoFormatLookup[i].pixelFormat == format) + return i; + + return -1; +} + +static int indexOfVideoFormat(GstVideoFormat format) +{ + for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) + if (qt_videoFormatLookup[i].gstFormat == format) + return i; + + return -1; +} + +} + +QVideoSurfaceFormat QGstUtils::formatForCaps( + GstCaps *caps, GstVideoInfo *info, QAbstractVideoBuffer::HandleType handleType) +{ + GstVideoInfo vidInfo; + GstVideoInfo *infoPtr = info ? info : &vidInfo; + + if (gst_video_info_from_caps(infoPtr, caps)) { + int index = indexOfVideoFormat(infoPtr->finfo->format); + + if (index != -1) { + QVideoSurfaceFormat format( + QSize(infoPtr->width, infoPtr->height), + qt_videoFormatLookup[index].pixelFormat, + handleType); + + if (infoPtr->fps_d > 0) + format.setFrameRate(qreal(infoPtr->fps_n) / infoPtr->fps_d); + + if (infoPtr->par_d > 0) + format.setPixelAspectRatio(infoPtr->par_n, infoPtr->par_d); + + return format; + } + } + return QVideoSurfaceFormat(); +} + +GstCaps *QGstUtils::capsForFormats(const QList &formats) +{ + GstCaps *caps = gst_caps_new_empty(); + + for (QVideoFrame::PixelFormat format : formats) { + int index = indexOfVideoFormat(format); + + if (index != -1) { + gst_caps_append_structure(caps, gst_structure_new( + "video/x-raw", + "format" , G_TYPE_STRING, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat), + nullptr)); + } + } + + gst_caps_set_simple( + caps, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + nullptr); + + return caps; +} + +void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) +{ + // GStreamer uses nanoseconds, Qt uses microseconds + qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); + if (startTime >= 0) { + frame->setStartTime(startTime/G_GINT64_CONSTANT (1000)); + + qint64 duration = GST_BUFFER_DURATION(buffer); + if (duration >= 0) + frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); + } +} + +void QGstUtils::setMetaData(GstElement *element, const QMap &data) +{ + if (!GST_IS_TAG_SETTER(element)) + return; + + gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); + + for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) { + const QString tagName = QString::fromLatin1(it.key()); + const QVariant &tagValue = it.value(); + + switch (tagValue.typeId()) { + case QMetaType::QString: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + tagValue.toString().toUtf8().constData(), + nullptr); + break; + case QMetaType::Int: + case QMetaType::LongLong: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + tagValue.toInt(), + nullptr); + break; + case QMetaType::Double: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + tagValue.toDouble(), + nullptr); + break; + case QMetaType::QDateTime: { + QDateTime date = tagValue.toDateTime().toLocalTime(); + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + gst_date_time_new_local_time( + date.date().year(), date.date().month(), date.date().day(), + date.time().hour(), date.time().minute(), date.time().second()), + nullptr); + break; + } + default: + break; + } + } +} + +void QGstUtils::setMetaData(GstBin *bin, const QMap &data) +{ + GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER); + GValue item = G_VALUE_INIT; + while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) { + GstElement * const element = GST_ELEMENT(g_value_get_object(&item)); + setMetaData(element, data); + } + gst_iterator_free(elements); +} + + +GstCaps *QGstUtils::videoFilterCaps() +{ + const char *caps = + "video/x-raw(ANY);" + "image/jpeg;" + "video/x-h264"; + static GstStaticCaps staticCaps = GST_STATIC_CAPS(caps); + + return gst_caps_make_writable(gst_static_caps_get(&staticCaps)); +} + +QSize QGstUtils::structureResolution(const GstStructure *s) +{ + QSize size; + + int w, h; + if (s && gst_structure_get_int(s, "width", &w) && gst_structure_get_int(s, "height", &h)) { + size.rwidth() = w; + size.rheight() = h; + } + + return size; +} + +QVideoFrame::PixelFormat QGstUtils::structurePixelFormat(const GstStructure *structure) +{ + QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; + + if (!structure) + return pixelFormat; + + if (gst_structure_has_name(structure, "video/x-raw")) { + const gchar *s = gst_structure_get_string(structure, "format"); + if (s) { + GstVideoFormat format = gst_video_format_from_string(s); + int index = indexOfVideoFormat(format); + + if (index != -1) + pixelFormat = qt_videoFormatLookup[index].pixelFormat; + } + } + + return pixelFormat; +} + +QSize QGstUtils::structurePixelAspectRatio(const GstStructure *s) +{ + QSize ratio(1, 1); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (s && gst_structure_get_fraction(s, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) { + ratio.rwidth() = aspectNum; + ratio.rheight() = aspectDenum; + } + } + + return ratio; +} + +QPair QGstUtils::structureFrameRateRange(const GstStructure *s) +{ + QPair rate; + + if (!s) + return rate; + + int n, d; + if (gst_structure_get_fraction(s, "framerate", &n, &d)) { + rate.second = qreal(n) / d; + rate.first = rate.second; + } else if (gst_structure_get_fraction(s, "max-framerate", &n, &d)) { + rate.second = qreal(n) / d; + if (gst_structure_get_fraction(s, "min-framerate", &n, &d)) + rate.first = qreal(n) / d; + else + rate.first = qreal(1); + } + + return rate; +} + +typedef QMap FileExtensionMap; +Q_GLOBAL_STATIC(FileExtensionMap, fileExtensionMap) + +QString QGstUtils::fileExtensionForMimeType(const QString &mimeType) +{ + if (fileExtensionMap->isEmpty()) { + //extension for containers hard to guess from mimetype + fileExtensionMap->insert(QStringLiteral("video/x-matroska"), QLatin1String("mkv")); + fileExtensionMap->insert(QStringLiteral("video/quicktime"), QLatin1String("mov")); + fileExtensionMap->insert(QStringLiteral("video/x-msvideo"), QLatin1String("avi")); + fileExtensionMap->insert(QStringLiteral("video/msvideo"), QLatin1String("avi")); + fileExtensionMap->insert(QStringLiteral("audio/mpeg"), QLatin1String("mp3")); + fileExtensionMap->insert(QStringLiteral("application/x-shockwave-flash"), QLatin1String("swf")); + fileExtensionMap->insert(QStringLiteral("application/x-pn-realmedia"), QLatin1String("rm")); + } + + //for container names like avi instead of video/x-msvideo, use it as extension + if (!mimeType.contains(QLatin1Char('/'))) + return mimeType; + + QString format = mimeType.left(mimeType.indexOf(QLatin1Char(','))); + QString extension = fileExtensionMap->value(format); + + if (!extension.isEmpty() || format.isEmpty()) + return extension; + + QRegularExpression rx(QStringLiteral("[-/]([\\w]+)$")); + QRegularExpressionMatch match = rx.match(format); + + if (match.hasMatch()) + extension = match.captured(1); + + return extension; +} + +QVariant QGstUtils::fromGStreamerOrientation(const QVariant &value) +{ + // Note gstreamer tokens either describe the counter clockwise rotation of the + // image or the clockwise transform to apply to correct the image. The orientation + // value returned is the clockwise rotation of the image. + const QString token = value.toString(); + if (token == QStringLiteral("rotate-90")) + return 270; + if (token == QStringLiteral("rotate-180")) + return 180; + if (token == QStringLiteral("rotate-270")) + return 90; + return 0; +} + +QVariant QGstUtils::toGStreamerOrientation(const QVariant &value) +{ + switch (value.toInt()) { + case 90: + return QStringLiteral("rotate-270"); + case 180: + return QStringLiteral("rotate-180"); + case 270: + return QStringLiteral("rotate-90"); + default: + return QStringLiteral("rotate-0"); + } +} + +bool QGstUtils::useOpenGL() +{ + static bool result = qEnvironmentVariableIntValue("QT_GSTREAMER_USE_OPENGL_PLUGIN"); + return result; +} + +void qt_gst_object_ref_sink(gpointer object) +{ + gst_object_ref_sink(object); +} + +GstCaps *qt_gst_pad_get_current_caps(GstPad *pad) +{ + return gst_pad_get_current_caps(pad); +} + +GstCaps *qt_gst_pad_get_caps(GstPad *pad) +{ + return gst_pad_query_caps(pad, nullptr); +} + +GstStructure *qt_gst_structure_new_empty(const char *name) +{ + return gst_structure_new_empty(name); +} + +gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur) +{ + return gst_element_query_position(element, format, cur); +} + +gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur) +{ + return gst_element_query_duration(element, format, cur); +} + +GstCaps *qt_gst_caps_normalize(GstCaps *caps) +{ + // gst_caps_normalize() takes ownership of the argument in 1.0 + return gst_caps_normalize(caps); +} + +const gchar *qt_gst_element_get_factory_name(GstElement *element) +{ + const gchar *name = 0; + const GstElementFactory *factory = 0; + + if (element && (factory = gst_element_get_factory(element))) + name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); + + return name; +} + +gboolean qt_gst_caps_can_intersect(const GstCaps * caps1, const GstCaps * caps2) +{ + return gst_caps_can_intersect(caps1, caps2); +} + +GList *qt_gst_video_sinks() +{ + GList *list = nullptr; + + list = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, + GST_RANK_MARGINAL); + + return list; +} + +void qt_gst_util_double_to_fraction(gdouble src, gint *dest_n, gint *dest_d) +{ + gst_util_double_to_fraction(src, dest_n, dest_d); +} + +QDebug operator <<(QDebug debug, GstCaps *caps) +{ + if (caps) { + gchar *string = gst_caps_to_string(caps); + debug = debug << string; + g_free(string); + } + return debug; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/gstreamer/qgstutils_p.h b/src/multimedia/gstreamer/qgstutils_p.h new file mode 100644 index 000000000..853064707 --- /dev/null +++ b/src/multimedia/gstreamer/qgstutils_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTUTILS_P_H +#define QGSTUTILS_P_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +# define QT_GSTREAMER_PLAYBIN_ELEMENT_NAME "playbin" +# define QT_GSTREAMER_CAMERABIN_ELEMENT_NAME "camerabin" +# define QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME "videoconvert" +# define QT_GSTREAMER_RAW_AUDIO_MIME "audio/x-raw" +# define QT_GSTREAMER_VIDEOOVERLAY_INTERFACE_NAME "GstVideoOverlay" + +QT_BEGIN_NAMESPACE + +class QSize; +class QVariant; +class QByteArray; +class QImage; +class QVideoSurfaceFormat; + +namespace QGstUtils { + struct Q_MULTIMEDIA_EXPORT CameraInfo + { + QString name; + QString description; + int orientation; + QCamera::Position position; + QByteArray driver; + }; + + Q_MULTIMEDIA_EXPORT QMap gstTagListToMap(const GstTagList *list); + + Q_MULTIMEDIA_EXPORT QSize capsResolution(const GstCaps *caps); + Q_MULTIMEDIA_EXPORT QSize capsCorrectedResolution(const GstCaps *caps); + Q_MULTIMEDIA_EXPORT QAudioFormat audioFormatForCaps(const GstCaps *caps); + Q_MULTIMEDIA_EXPORT QAudioFormat audioFormatForSample(GstSample *sample); + Q_MULTIMEDIA_EXPORT GstCaps *capsForAudioFormat(const QAudioFormat &format); + Q_MULTIMEDIA_EXPORT void initializeGst(); + Q_MULTIMEDIA_EXPORT QMultimedia::SupportEstimate hasSupport(const QString &mimeType, + const QStringList &codecs, + const QSet &supportedMimeTypeSet); + + Q_MULTIMEDIA_EXPORT QList enumerateCameras(); + Q_MULTIMEDIA_EXPORT QList cameraDevices(); + Q_MULTIMEDIA_EXPORT QString cameraDescription(const QString &device); + Q_MULTIMEDIA_EXPORT QCamera::Position cameraPosition(const QString &device); + Q_MULTIMEDIA_EXPORT int cameraOrientation(const QString &device); + Q_MULTIMEDIA_EXPORT QByteArray cameraDriver(const QString &device); + + Q_MULTIMEDIA_EXPORT const QSet &audioSources(); + Q_MULTIMEDIA_EXPORT const QSet &audioSinks(); + + Q_MULTIMEDIA_EXPORT QSet supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)); + + Q_MULTIMEDIA_EXPORT QImage bufferToImage(GstBuffer *buffer, const GstVideoInfo &info); + Q_MULTIMEDIA_EXPORT QVideoSurfaceFormat formatForCaps( + GstCaps *caps, + GstVideoInfo *info = 0, + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); + + Q_MULTIMEDIA_EXPORT GstCaps *capsForFormats(const QList &formats); + void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); + + Q_MULTIMEDIA_EXPORT void setMetaData(GstElement *element, const QMap &data); + Q_MULTIMEDIA_EXPORT void setMetaData(GstBin *bin, const QMap &data); + + Q_MULTIMEDIA_EXPORT GstCaps *videoFilterCaps(); + + Q_MULTIMEDIA_EXPORT QSize structureResolution(const GstStructure *s); + Q_MULTIMEDIA_EXPORT QVideoFrame::PixelFormat structurePixelFormat(const GstStructure *s); + Q_MULTIMEDIA_EXPORT QSize structurePixelAspectRatio(const GstStructure *s); + Q_MULTIMEDIA_EXPORT QPair structureFrameRateRange(const GstStructure *s); + + Q_MULTIMEDIA_EXPORT QString fileExtensionForMimeType(const QString &mimeType); + + Q_MULTIMEDIA_EXPORT QVariant fromGStreamerOrientation(const QVariant &value); + Q_MULTIMEDIA_EXPORT QVariant toGStreamerOrientation(const QVariant &value); + + Q_MULTIMEDIA_EXPORT bool useOpenGL(); +} + +Q_MULTIMEDIA_EXPORT void qt_gst_object_ref_sink(gpointer object); +Q_MULTIMEDIA_EXPORT GstCaps *qt_gst_pad_get_current_caps(GstPad *pad); +Q_MULTIMEDIA_EXPORT GstCaps *qt_gst_pad_get_caps(GstPad *pad); +Q_MULTIMEDIA_EXPORT GstStructure *qt_gst_structure_new_empty(const char *name); +Q_MULTIMEDIA_EXPORT gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur); +Q_MULTIMEDIA_EXPORT gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur); +Q_MULTIMEDIA_EXPORT GstCaps *qt_gst_caps_normalize(GstCaps *caps); +Q_MULTIMEDIA_EXPORT const gchar *qt_gst_element_get_factory_name(GstElement *element); +Q_MULTIMEDIA_EXPORT gboolean qt_gst_caps_can_intersect(const GstCaps * caps1, const GstCaps * caps2); +Q_MULTIMEDIA_EXPORT GList *qt_gst_video_sinks(); +Q_MULTIMEDIA_EXPORT void qt_gst_util_double_to_fraction(gdouble src, gint *dest_n, gint *dest_d); + +Q_MULTIMEDIA_EXPORT QDebug operator <<(QDebug debug, GstCaps *caps); + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstvideobuffer.cpp b/src/multimedia/gstreamer/qgstvideobuffer.cpp new file mode 100644 index 000000000..58738ffa0 --- /dev/null +++ b/src/multimedia/gstreamer/qgstvideobuffer.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** 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 "qgstvideobuffer_p.h" + +QT_BEGIN_NAMESPACE + +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info) + : QAbstractVideoBuffer(NoHandle) + , m_videoInfo(info) + , m_buffer(buffer) +{ + gst_buffer_ref(m_buffer); +} + +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, + QGstVideoBuffer::HandleType handleType, + const QVariant &handle) + : QAbstractVideoBuffer(handleType) + , m_videoInfo(info) + , m_buffer(buffer) + , m_handle(handle) +{ + gst_buffer_ref(m_buffer); +} + +QGstVideoBuffer::~QGstVideoBuffer() +{ + unmap(); + + gst_buffer_unref(m_buffer); +} + + +QAbstractVideoBuffer::MapMode QGstVideoBuffer::mapMode() const +{ + return m_mode; +} + +QAbstractVideoBuffer::MapData QGstVideoBuffer::map(MapMode mode) +{ + const GstMapFlags flags = GstMapFlags(((mode & ReadOnly) ? GST_MAP_READ : 0) + | ((mode & WriteOnly) ? GST_MAP_WRITE : 0)); + + MapData mapData; + if (mode == NotMapped || m_mode != NotMapped) + return mapData; + + if (m_videoInfo.finfo->n_planes == 0) { // Encoded + if (gst_buffer_map(m_buffer, &m_frame.map[0], flags)) { + mapData.nBytes = m_frame.map[0].size; + mapData.nPlanes = 1; + mapData.bytesPerLine[0] = -1; + mapData.data[0] = static_cast(m_frame.map[0].data); + + m_mode = mode; + } + } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, flags)) { + mapData.nBytes = m_frame.info.size; + mapData.nPlanes = m_frame.info.finfo->n_planes; + + for (guint i = 0; i < m_frame.info.finfo->n_planes; ++i) { + mapData.bytesPerLine[i] = m_frame.info.stride[i]; + mapData.data[i] = static_cast(m_frame.data[i]); + } + + m_mode = mode; + } + return mapData; +} + +void QGstVideoBuffer::unmap() +{ + if (m_mode != NotMapped) { + if (m_videoInfo.finfo->n_planes == 0) + gst_buffer_unmap(m_buffer, &m_frame.map[0]); + else + gst_video_frame_unmap(&m_frame); + } + m_mode = NotMapped; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/gstreamer/qgstvideobuffer_p.h b/src/multimedia/gstreamer/qgstvideobuffer_p.h new file mode 100644 index 000000000..11a217f51 --- /dev/null +++ b/src/multimedia/gstreamer/qgstvideobuffer_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTVIDEOBUFFER_P_H +#define QGSTVIDEOBUFFER_P_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 +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstVideoBuffer : public QAbstractVideoBuffer +{ +public: + QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info); + QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, + HandleType handleType, const QVariant &handle); + + ~QGstVideoBuffer(); + + GstBuffer *buffer() const { return m_buffer; } + MapMode mapMode() const override; + + MapData map(MapMode mode) override; + void unmap() override; + + QVariant handle() const override { return m_handle; } +private: + GstVideoInfo m_videoInfo; + GstVideoFrame m_frame; + GstBuffer *m_buffer = nullptr; + MapMode m_mode = NotMapped; + QVariant m_handle; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstvideorendererplugin.cpp b/src/multimedia/gstreamer/qgstvideorendererplugin.cpp new file mode 100644 index 000000000..1b63cbfa8 --- /dev/null +++ b/src/multimedia/gstreamer/qgstvideorendererplugin.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla 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 "qgstvideorendererplugin_p.h" + +QT_BEGIN_NAMESPACE + +QGstVideoRendererPlugin::QGstVideoRendererPlugin(QObject *parent) : + QObject(parent) +{ +} + +QGstVideoRenderer::~QGstVideoRenderer() {} + +QGstVideoRendererInterface::~QGstVideoRendererInterface() {} + +QGstVideoRendererPlugin::~QGstVideoRendererPlugin() {} + +QT_END_NAMESPACE + +#include "moc_qgstvideorendererplugin_p.cpp" + + diff --git a/src/multimedia/gstreamer/qgstvideorendererplugin_p.h b/src/multimedia/gstreamer/qgstvideorendererplugin_p.h new file mode 100644 index 000000000..d6f13062e --- /dev/null +++ b/src/multimedia/gstreamer/qgstvideorendererplugin_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla 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 QGSTVIDEORENDERERPLUGIN_P_H +#define QGSTVIDEORENDERERPLUGIN_P_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 +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QAbstractVideoSurface; + +#ifndef Q_MULTIMEDIA_EXPORT +#error XXX +#endif + +class Q_MULTIMEDIA_EXPORT QGstVideoRenderer +{ +public: + virtual ~QGstVideoRenderer(); + + virtual GstCaps *getCaps(QAbstractVideoSurface *surface) = 0; + virtual bool start(QAbstractVideoSurface *surface, GstCaps *caps) = 0; + virtual void stop(QAbstractVideoSurface *surface) = 0; // surface may be null if unexpectedly deleted. + virtual bool proposeAllocation(GstQuery *query) = 0; // may be called from a thread. + + virtual bool present(QAbstractVideoSurface *surface, GstBuffer *buffer) = 0; + virtual void flush(QAbstractVideoSurface *surface) = 0; // surface may be null if unexpectedly deleted. +}; + +/* + Abstract interface for video buffers allocation. +*/ +class Q_MULTIMEDIA_EXPORT QGstVideoRendererInterface +{ +public: + virtual ~QGstVideoRendererInterface(); + + virtual QGstVideoRenderer *createRenderer() = 0; +}; + +#define QGstVideoRendererInterface_iid "org.qt-project.qt.gstvideorenderer/5.4" +Q_DECLARE_INTERFACE(QGstVideoRendererInterface, QGstVideoRendererInterface_iid) + +class Q_MULTIMEDIA_EXPORT QGstVideoRendererPlugin : public QObject, public QGstVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstVideoRendererInterface) +public: + explicit QGstVideoRendererPlugin(QObject *parent = 0); + virtual ~QGstVideoRendererPlugin(); + + QGstVideoRenderer *createRenderer() override = 0; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gstreamer/qgstvideorenderersink.cpp b/src/multimedia/gstreamer/qgstvideorenderersink.cpp new file mode 100644 index 000000000..0f930cf02 --- /dev/null +++ b/src/multimedia/gstreamer/qgstvideorenderersink.cpp @@ -0,0 +1,803 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla 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 +#include +#include +#include +#include +#include +#include + +#include +#include "qgstvideobuffer_p.h" + +#include "qgstvideorenderersink_p.h" + +#include + +#include "qgstutils_p.h" + +#if QT_CONFIG(gstreamer_gl) +#include +#include +#include +#include + +#include + +#if GST_GL_HAVE_WINDOW_X11 +# include +#endif +#if GST_GL_HAVE_PLATFORM_EGL +# include +#endif +#if GST_GL_HAVE_WINDOW_WAYLAND +# include +#endif +#endif // #if QT_CONFIG(gstreamer_gl) + +//#define DEBUG_VIDEO_SURFACE_SINK + +QT_BEGIN_NAMESPACE + +QGstDefaultVideoRenderer::QGstDefaultVideoRenderer() +{ +} + +QGstDefaultVideoRenderer::~QGstDefaultVideoRenderer() +{ +} + +GstCaps *QGstDefaultVideoRenderer::getCaps(QAbstractVideoSurface *surface) +{ +#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_handleType); + + return m_format.isValid() && surface->start(m_format); +} + +void QGstDefaultVideoRenderer::stop(QAbstractVideoSurface *surface) +{ + m_flushed = true; + if (surface) + surface->stop(); +} + +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); + + auto meta = gst_buffer_get_video_crop_meta (buffer); + if (meta) { + QRect vp(meta->x, meta->y, meta->width, meta->height); + if (m_format.viewport() != vp) { +#ifdef DEBUG_VIDEO_SURFACE_SINK + qDebug() << Q_FUNC_INFO << " Update viewport on Metadata: [" << meta->height << "x" << meta->width << " | " << meta->x << "x" << meta->y << "]"; +#endif + //Update viewport if data is not the same + m_format.setViewport(vp); + surface->start(m_format); + } + } + + QVideoFrame frame( + videoBuffer, + m_format.frameSize(), + m_format.pixelFormat()); + QGstUtils::setFrameTimeStamps(&frame, buffer); + + return surface->present(frame); +} + +void QGstDefaultVideoRenderer::flush(QAbstractVideoSurface *surface) +{ + if (surface && !m_flushed) + surface->present(QVideoFrame()); + m_flushed = true; +} + +bool QGstDefaultVideoRenderer::proposeAllocation(GstQuery *) +{ + return true; +} + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, rendererLoader, + (QGstVideoRendererInterface_iid, QLatin1String("video/gstvideorenderer"), Qt::CaseInsensitive)) + +QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface) + : m_surface(surface) +{ + int i = 0; + while (QObject *instance = rendererLoader->instance(i)) { + auto plugin = qobject_cast(instance); + if (QGstVideoRenderer *renderer = plugin ? plugin->createRenderer() : nullptr) + m_renderers.append(renderer); + ++i; + } + + m_renderers.append(new QGstDefaultVideoRenderer); + updateSupportedFormats(); + connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats())); +} + +QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate() +{ + qDeleteAll(m_renderers); + + if (m_surfaceCaps) + 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() +{ + QMutexLocker locker(&m_mutex); + + gst_caps_ref(m_surfaceCaps); + + return m_surfaceCaps; +} + +bool QVideoSurfaceGstDelegate::start(GstCaps *caps) +{ + QMutexLocker locker(&m_mutex); + + if (m_activeRenderer) { + m_flush = true; + m_stop = true; + } + + if (m_startCaps) + gst_caps_unref(m_startCaps); + m_startCaps = caps; + gst_caps_ref(m_startCaps); + + /* + Waiting for start() to be invoked in the main thread may block + if gstreamer blocks the main thread until this call is finished. + This situation is rare and usually caused by setState(Null) + while pipeline is being prerolled. + + The proper solution to this involves controlling gstreamer pipeline from + other thread than video surface. + + Currently start() fails if wait() timed out. + */ + if (!waitForAsyncEvent(&locker, &m_setupCondition, 1000) && m_startCaps) { + qWarning() << "Failed to start video surface due to main thread blocked."; + gst_caps_unref(m_startCaps); + m_startCaps = 0; + } + + return m_activeRenderer != 0; +} + +void QVideoSurfaceGstDelegate::stop() +{ + QMutexLocker locker(&m_mutex); + + if (!m_activeRenderer) + return; + + m_flush = true; + m_stop = true; + + if (m_startCaps) { + gst_caps_unref(m_startCaps); + m_startCaps = 0; + } + + waitForAsyncEvent(&locker, &m_setupCondition, 500); +} + +void QVideoSurfaceGstDelegate::unlock() +{ + QMutexLocker locker(&m_mutex); + + m_setupCondition.wakeAll(); + m_renderCondition.wakeAll(); +} + +bool QVideoSurfaceGstDelegate::proposeAllocation(GstQuery *query) +{ + QMutexLocker locker(&m_mutex); + + if (QGstVideoRenderer *pool = m_activeRenderer) { + locker.unlock(); + + return pool->proposeAllocation(query); + } + + return false; +} + +void QVideoSurfaceGstDelegate::flush() +{ + QMutexLocker locker(&m_mutex); + + m_flush = true; + m_renderBuffer = 0; + m_renderCondition.wakeAll(); + + notify(); +} + +GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) +{ + QMutexLocker locker(&m_mutex); + + m_renderReturn = GST_FLOW_OK; + m_renderBuffer = buffer; + + waitForAsyncEvent(&locker, &m_renderCondition, 300); + + m_renderBuffer = 0; + + return m_renderReturn; +} + +#if QT_CONFIG(gstreamer_gl) +static GstGLContext *gstGLDisplayContext(QAbstractVideoSurface *surface) +{ + auto glContext = qobject_cast(surface->property("GLContext").value()); + // 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_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 + + 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 = nullptr; + 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 = nullptr; + gst_query_parse_context(query, &context); + context = context ? gst_context_copy(context) : gst_context_new(type, FALSE); + GstStructure *structure = gst_context_writable_structure(context); + gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, m_gstGLDisplayContext, nullptr); + 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) { + QMutexLocker locker(&m_mutex); + + if (m_notified) { + while (handleEvent(&locker)) {} + m_notified = false; + } + return true; + } + + return QObject::event(event); +} + +bool QVideoSurfaceGstDelegate::handleEvent(QMutexLocker *locker) +{ + if (m_flush) { + m_flush = false; + if (m_activeRenderer) { + locker->unlock(); + + m_activeRenderer->flush(m_surface); + } + } else if (m_stop) { + m_stop = false; + + if (QGstVideoRenderer * const activePool = m_activeRenderer) { + m_activeRenderer = 0; + locker->unlock(); + + activePool->stop(m_surface); + + locker->relock(); + } + } else if (m_startCaps) { + Q_ASSERT(!m_activeRenderer); + + GstCaps * const startCaps = m_startCaps; + m_startCaps = 0; + + if (m_renderer && m_surface) { + locker->unlock(); + + const bool started = m_renderer->start(m_surface, startCaps); + + locker->relock(); + + m_activeRenderer = started + ? m_renderer + : 0; + } else if (QGstVideoRenderer * const activePool = m_activeRenderer) { + m_activeRenderer = 0; + locker->unlock(); + + activePool->stop(m_surface); + + locker->relock(); + } + + gst_caps_unref(startCaps); + } else if (m_renderBuffer) { + GstBuffer *buffer = m_renderBuffer; + m_renderBuffer = 0; + m_renderReturn = GST_FLOW_ERROR; + + if (m_activeRenderer && m_surface) { + gst_buffer_ref(buffer); + + locker->unlock(); + + const bool rendered = m_activeRenderer->present(m_surface, buffer); + + gst_buffer_unref(buffer); + + locker->relock(); + + if (rendered) + m_renderReturn = GST_FLOW_OK; + } + + m_renderCondition.wakeAll(); + } else { + m_setupCondition.wakeAll(); + + return false; + } + return true; +} + +void QVideoSurfaceGstDelegate::notify() +{ + if (!m_notified) { + m_notified = true; + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } +} + +bool QVideoSurfaceGstDelegate::waitForAsyncEvent( + QMutexLocker *locker, QWaitCondition *condition, unsigned long time) +{ + if (QThread::currentThread() == thread()) { + while (handleEvent(locker)) {} + m_notified = false; + + return true; + } + + notify(); + + return condition->wait(&m_mutex, time); +} + +void QVideoSurfaceGstDelegate::updateSupportedFormats() +{ + if (m_surfaceCaps) { + gst_caps_unref(m_surfaceCaps); + m_surfaceCaps = 0; + } + + for (QGstVideoRenderer *pool : qAsConst(m_renderers)) { + if (GstCaps *caps = pool->getCaps(m_surface)) { + if (gst_caps_is_empty(caps)) { + gst_caps_unref(caps); + continue; + } + + if (m_surfaceCaps) + gst_caps_unref(m_surfaceCaps); + + m_renderer = pool; + m_surfaceCaps = caps; + break; + } + } +} + +static GstVideoSinkClass *sink_parent_class; +static QAbstractVideoSurface *current_surface; + +#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast(s)) + +QGstVideoRendererSink *QGstVideoRendererSink::createSink(QAbstractVideoSurface *surface) +{ + setSurface(surface); + QGstVideoRendererSink *sink = reinterpret_cast( + g_object_new(QGstVideoRendererSink::get_type(), 0)); + + g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); + + return sink; +} + +void QGstVideoRendererSink::setSurface(QAbstractVideoSurface *surface) +{ + current_surface = surface; + get_type(); +} + +GType QGstVideoRendererSink::get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = + { + sizeof(QGstVideoRendererSinkClass), // class_size + base_init, // base_init + nullptr, // base_finalize + class_init, // class_init + nullptr, // class_finalize + nullptr, // class_data + sizeof(QGstVideoRendererSink), // instance_size + 0, // n_preallocs + instance_init, // instance_init + 0 // value_table + }; + + type = g_type_register_static( + GST_TYPE_VIDEO_SINK, "QGstVideoRendererSink", &info, GTypeFlags(0)); + + // Register the sink type to be used in custom piplines. + // When surface is ready the sink can be used. + gst_element_register(nullptr, "qtvideosink", GST_RANK_PRIMARY, type); + } + + return type; +} + +void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + + sink_parent_class = reinterpret_cast(g_type_class_peek_parent(g_class)); + + GstVideoSinkClass *video_sink_class = reinterpret_cast(g_class); + video_sink_class->show_frame = QGstVideoRendererSink::show_frame; + + GstBaseSinkClass *base_sink_class = reinterpret_cast(g_class); + base_sink_class->get_caps = QGstVideoRendererSink::get_caps; + base_sink_class->set_caps = QGstVideoRendererSink::set_caps; + 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(g_class); + element_class->change_state = QGstVideoRendererSink::change_state; + gst_element_class_set_metadata(element_class, + "Qt built-in video renderer sink", + "Sink/Video", + "Qt default built-in video renderer sink", + "The Qt Company"); + + GObjectClass *object_class = reinterpret_cast(g_class); + object_class->finalize = QGstVideoRendererSink::finalize; +} + +void QGstVideoRendererSink::base_init(gpointer g_class) +{ + static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( + "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( + "video/x-raw, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]")); + + gst_element_class_add_pad_template( + GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); +} + +struct NullSurface : QAbstractVideoSurface +{ + NullSurface(QObject *parent = nullptr) : QAbstractVideoSurface(parent) { } + + QList supportedPixelFormats(QAbstractVideoBuffer::HandleType) const override + { + return QList() << QVideoFrame::Format_RGB32; + } + + bool present(const QVideoFrame &) override + { + return true; + } +}; + +void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class) +{ + Q_UNUSED(g_class); + VO_SINK(instance); + + if (!current_surface) { + qWarning() << "Using qtvideosink element without video surface"; + static NullSurface nullSurface; + current_surface = &nullSurface; + } + + sink->delegate = new QVideoSurfaceGstDelegate(current_surface); + sink->delegate->moveToThread(current_surface->thread()); + current_surface = nullptr; +} + +void QGstVideoRendererSink::finalize(GObject *object) +{ + VO_SINK(object); + + delete sink->delegate; + + // Chain up + G_OBJECT_CLASS(sink_parent_class)->finalize(object); +} + +void QGstVideoRendererSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(o); + Q_UNUSED(p); + QGstVideoRendererSink *sink = reinterpret_cast(d); + + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, nullptr); + + if (!showPrerollFrame) { + GstState state = GST_STATE_VOID_PENDING; + GstClockTime timeout = 10000000; // 10 ms + gst_element_get_state(GST_ELEMENT(sink), &state, nullptr, timeout); + // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means + // the QMediaPlayer was stopped from the paused state. + // We need to flush the current frame. + if (state == GST_STATE_PAUSED) + sink->delegate->flush(); + } +} + +GstStateChangeReturn QGstVideoRendererSink::change_state( + GstElement *element, GstStateChange transition) +{ + QGstVideoRendererSink *sink = reinterpret_cast(element); + + gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, nullptr); + + // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to + // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. + // We need to flush the current frame. + if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) + sink->delegate->flush(); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); +} + +GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter) +{ + VO_SINK(base); + + GstCaps *caps = sink->delegate->caps(); + GstCaps *unfiltered = caps; + if (filter) { + caps = gst_caps_intersect(unfiltered, filter); + gst_caps_unref(unfiltered); + } + + return caps; +} + +gboolean QGstVideoRendererSink::set_caps(GstBaseSink *base, GstCaps *caps) +{ + VO_SINK(base); + +#ifdef DEBUG_VIDEO_SURFACE_SINK + qDebug() << "set_caps:"; + qDebug() << caps; +#endif + + if (!caps) { + sink->delegate->stop(); + + return TRUE; + } else if (sink->delegate->start(caps)) { + return TRUE; + } else { + return FALSE; + } +} + +gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *base, GstQuery *query) +{ + VO_SINK(base); + return sink->delegate->proposeAllocation(query); +} + +gboolean QGstVideoRendererSink::stop(GstBaseSink *base) +{ + VO_SINK(base); + sink->delegate->stop(); + return TRUE; +} + +gboolean QGstVideoRendererSink::unlock(GstBaseSink *base) +{ + VO_SINK(base); + sink->delegate->unlock(); + return TRUE; +} + +GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + 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/multimedia/gstreamer/qgstvideorenderersink_p.h b/src/multimedia/gstreamer/qgstvideorenderersink_p.h new file mode 100644 index 000000000..0b05bbe83 --- /dev/null +++ b/src/multimedia/gstreamer/qgstvideorenderersink_p.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla 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 QGSTVIDEORENDERERSINK_P_H +#define QGSTVIDEORENDERERSINK_P_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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qgstvideorendererplugin_p.h" + +#include "qgstvideorendererplugin_p.h" + +#if QT_CONFIG(gstreamer_gl) +#ifndef GST_USE_UNSTABLE_API +#define GST_USE_UNSTABLE_API +#endif +#include +#endif + +QT_BEGIN_NAMESPACE +class QAbstractVideoSurface; + +class QGstDefaultVideoRenderer : public QGstVideoRenderer +{ +public: + QGstDefaultVideoRenderer(); + ~QGstDefaultVideoRenderer(); + + GstCaps *getCaps(QAbstractVideoSurface *surface) override; + bool start(QAbstractVideoSurface *surface, GstCaps *caps) override; + void stop(QAbstractVideoSurface *surface) override; + + bool proposeAllocation(GstQuery *query) override; + + bool present(QAbstractVideoSurface *surface, GstBuffer *buffer) override; + void flush(QAbstractVideoSurface *surface) override; + +private: + QVideoSurfaceFormat m_format; + GstVideoInfo m_videoInfo; + bool m_flushed = true; + QAbstractVideoBuffer::HandleType m_handleType = QAbstractVideoBuffer::NoHandle; +}; + +class QVideoSurfaceGstDelegate : public QObject +{ + Q_OBJECT +public: + QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface); + ~QVideoSurfaceGstDelegate(); + + GstCaps *caps(); + + bool start(GstCaps *caps); + void stop(); + void unlock(); + bool proposeAllocation(GstQuery *query); + + void flush(); + + GstFlowReturn render(GstBuffer *buffer); + + bool event(QEvent *event) override; + bool query(GstQuery *query); + +private slots: + bool handleEvent(QMutexLocker *locker); + void updateSupportedFormats(); + +private: + void notify(); + bool waitForAsyncEvent(QMutexLocker *locker, QWaitCondition *condition, unsigned long time); + + QPointer m_surface; + + QMutex m_mutex; + QWaitCondition m_setupCondition; + QWaitCondition m_renderCondition; + GstFlowReturn m_renderReturn = GST_FLOW_OK; + QList m_renderers; + QGstVideoRenderer *m_renderer = nullptr; + QGstVideoRenderer *m_activeRenderer = nullptr; + + 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; + bool m_flush = false; +}; + +class Q_MULTIMEDIA_EXPORT QGstVideoRendererSink +{ +public: + GstVideoSink parent; + + static QGstVideoRendererSink *createSink(QAbstractVideoSurface *surface); + static void setSurface(QAbstractVideoSurface *surface); + +private: + static GType get_type(); + static void class_init(gpointer g_class, gpointer class_data); + static void base_init(gpointer g_class); + static void instance_init(GTypeInstance *instance, gpointer g_class); + + static void finalize(GObject *object); + + static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); + + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); + + static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter); + static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); + + static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query); + + static gboolean stop(GstBaseSink *sink); + + static gboolean unlock(GstBaseSink *sink); + + static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer); + static gboolean query(GstBaseSink *element, GstQuery *query); + +private: + QVideoSurfaceGstDelegate *delegate = nullptr; +}; + + +class QGstVideoRendererSinkClass +{ +public: + GstVideoSinkClass parent_class; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro index e0874c4aa..7ddcbcc61 100644 --- a/src/multimedia/multimedia.pro +++ b/src/multimedia/multimedia.pro @@ -74,6 +74,7 @@ MODULE_WINRT_CAPABILITIES_DEVICE += \ webcam qtConfig(gstreamer) { + include(gstreamer/gstreamer.pri) ANDROID_LIB_DEPENDENCIES += \ plugins/mediaservice/libgstcamerabin.so \ plugins/mediaservice/libgstmediacapture.so \ diff --git a/src/multimediawidgets/CMakeLists.txt b/src/multimediawidgets/CMakeLists.txt index 313027ebc..7b159d659 100644 --- a/src/multimediawidgets/CMakeLists.txt +++ b/src/multimediawidgets/CMakeLists.txt @@ -38,6 +38,11 @@ qt_extend_target(MultimediaWidgets CONDITION QT_FEATURE_graphicsview qgraphicsvideoitem.cpp qgraphicsvideoitem.h # special case ) +qt_extend_target(MultimediaWidgets CONDITION QT_FEATURE_gstreamer + SOURCES + gstreamer/qgstreamervideowidget.cpp gstreamer/qgstreamervideowidget_p.h +) + #### Keys ignored in scope 3:.:.:multimediawidgets.pro:QT_FEATURE_graphicsview: # PUBLIC_HEADERS = "qgraphicsvideoitem.h" diff --git a/src/multimediawidgets/gstreamer/gstreamer.pri b/src/multimediawidgets/gstreamer/gstreamer.pri new file mode 100644 index 000000000..dc332c249 --- /dev/null +++ b/src/multimediawidgets/gstreamer/gstreamer.pri @@ -0,0 +1,4 @@ +QMAKE_USE_PRIVATE += gstreamer + +SOURCES += gstreamer/qgstreamervideowidget.cpp +HEADERS += gstreamer/qgstreamervideowidget_p.h diff --git a/src/multimediawidgets/gstreamer/qgstreamervideowidget.cpp b/src/multimediawidgets/gstreamer/qgstreamervideowidget.cpp new file mode 100644 index 000000000..e1c8fe47c --- /dev/null +++ b/src/multimediawidgets/gstreamer/qgstreamervideowidget.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** 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 "qgstreamervideowidget_p.h" +#include "private/qgstutils_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoWidget : public QWidget +{ +public: + QGstreamerVideoWidget(QWidget *parent = 0) + :QWidget(parent) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QPalette palette; + palette.setColor(QPalette::Window, Qt::black); + setPalette(palette); + } + + virtual ~QGstreamerVideoWidget() {} + + QSize sizeHint() const override + { + return m_nativeSize; + } + + void setNativeSize( const QSize &size) + { + if (size != m_nativeSize) { + m_nativeSize = size; + if (size.isEmpty()) + setMinimumSize(0,0); + else + setMinimumSize(160,120); + + updateGeometry(); + } + } + + void paint_helper() + { + QPainter painter(this); + painter.fillRect(rect(), palette().window()); + } + +protected: + void paintEvent(QPaintEvent *) override + { + paint_helper(); + } + + QSize m_nativeSize; +}; + +QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *parent, const QByteArray &elementName) + : QVideoWidgetControl(parent) + , m_videoOverlay(this, !elementName.isEmpty() ? elementName : qgetenv("QT_GSTREAMER_WIDGET_VIDEOSINK")) +{ + connect(&m_videoOverlay, &QGstreamerVideoOverlay::activeChanged, + this, &QGstreamerVideoWidgetControl::onOverlayActiveChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::nativeVideoSizeChanged, + this, &QGstreamerVideoWidgetControl::onNativeVideoSizeChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::brightnessChanged, + this, &QGstreamerVideoWidgetControl::brightnessChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::contrastChanged, + this, &QGstreamerVideoWidgetControl::contrastChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::hueChanged, + this, &QGstreamerVideoWidgetControl::hueChanged); + connect(&m_videoOverlay, &QGstreamerVideoOverlay::saturationChanged, + this, &QGstreamerVideoWidgetControl::saturationChanged); +} + +QGstreamerVideoWidgetControl::~QGstreamerVideoWidgetControl() +{ + delete m_widget; +} + +void QGstreamerVideoWidgetControl::createVideoWidget() +{ + if (m_widget) + return; + + m_widget = new QGstreamerVideoWidget; + + m_widget->installEventFilter(this); + m_videoOverlay.setWindowHandle(m_windowId = m_widget->winId()); +} + +GstElement *QGstreamerVideoWidgetControl::videoSink() +{ + return m_videoOverlay.videoSink(); +} + +void QGstreamerVideoWidgetControl::setVideoSink(GstElement *sink) +{ + m_videoOverlay.setVideoSink(sink); +} + +void QGstreamerVideoWidgetControl::onOverlayActiveChanged() +{ + updateWidgetAttributes(); +} + +void QGstreamerVideoWidgetControl::stopRenderer() +{ + m_stopped = true; + updateWidgetAttributes(); + m_widget->setNativeSize(QSize()); +} + +void QGstreamerVideoWidgetControl::onNativeVideoSizeChanged() +{ + const QSize &size = m_videoOverlay.nativeVideoSize(); + + if (size.isValid()) + m_stopped = false; + + if (m_widget) + m_widget->setNativeSize(size); +} + +bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e) +{ + if (m_widget && object == m_widget) { + if (e->type() == QEvent::ParentChange || e->type() == QEvent::Show || e->type() == QEvent::WinIdChange) { + WId newWId = m_widget->winId(); + if (newWId != m_windowId) + m_videoOverlay.setWindowHandle(m_windowId = newWId); + } + + if (e->type() == QEvent::Paint) { + // Update overlay by new size if any. + if (QGstUtils::useOpenGL()) + 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 + m_widget->paint_helper(); // paints the black background + + return true; + } + } + + return false; +} + +void QGstreamerVideoWidgetControl::updateWidgetAttributes() +{ + // When frames are being rendered (sink is active), we need the WA_PaintOnScreen attribute to + // be set in order to avoid flickering when the widget is repainted (for example when resized). + // We need to clear that flag when the the sink is inactive to allow the widget to paint its + // background, otherwise some garbage will be displayed. + if (m_videoOverlay.isActive() && !m_stopped) { + m_widget->setAttribute(Qt::WA_NoSystemBackground, true); + m_widget->setAttribute(Qt::WA_PaintOnScreen, true); + } else { + m_widget->setAttribute(Qt::WA_NoSystemBackground, false); + m_widget->setAttribute(Qt::WA_PaintOnScreen, false); + m_widget->update(); + } +} + +bool QGstreamerVideoWidgetControl::processSyncMessage(const QGstreamerMessage &message) +{ + return m_videoOverlay.processSyncMessage(message); +} + +bool QGstreamerVideoWidgetControl::processBusMessage(const QGstreamerMessage &message) +{ + return m_videoOverlay.processBusMessage(message); +} + +QWidget *QGstreamerVideoWidgetControl::videoWidget() +{ + createVideoWidget(); + return m_widget; +} + +Qt::AspectRatioMode QGstreamerVideoWidgetControl::aspectRatioMode() const +{ + return m_videoOverlay.aspectRatioMode(); +} + +void QGstreamerVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_videoOverlay.setAspectRatioMode(mode); +} + +bool QGstreamerVideoWidgetControl::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoWidgetControl::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +int QGstreamerVideoWidgetControl::brightness() const +{ + return m_videoOverlay.brightness(); +} + +void QGstreamerVideoWidgetControl::setBrightness(int brightness) +{ + m_videoOverlay.setBrightness(brightness); +} + +int QGstreamerVideoWidgetControl::contrast() const +{ + return m_videoOverlay.contrast(); +} + +void QGstreamerVideoWidgetControl::setContrast(int contrast) +{ + m_videoOverlay.setContrast(contrast); +} + +int QGstreamerVideoWidgetControl::hue() const +{ + return m_videoOverlay.hue(); +} + +void QGstreamerVideoWidgetControl::setHue(int hue) +{ + m_videoOverlay.setHue(hue); +} + +int QGstreamerVideoWidgetControl::saturation() const +{ + return m_videoOverlay.saturation(); +} + +void QGstreamerVideoWidgetControl::setSaturation(int saturation) +{ + m_videoOverlay.setSaturation(saturation); +} + +QT_END_NAMESPACE diff --git a/src/multimediawidgets/gstreamer/qgstreamervideowidget_p.h b/src/multimediawidgets/gstreamer/qgstreamervideowidget_p.h new file mode 100644 index 000000000..38985ebd0 --- /dev/null +++ b/src/multimediawidgets/gstreamer/qgstreamervideowidget_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOWIDGET_H +#define QGSTREAMERVIDEOWIDGET_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 +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGstreamerVideoWidget; + +class Q_MULTIMEDIA_EXPORT QGstreamerVideoWidgetControl + : public QVideoWidgetControl + , public QGstreamerVideoRendererInterface + , public QGstreamerSyncMessageFilter + , public QGstreamerBusMessageFilter +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter QGstreamerBusMessageFilter) +public: + explicit QGstreamerVideoWidgetControl(QObject *parent = 0, const QByteArray &elementName = QByteArray()); + virtual ~QGstreamerVideoWidgetControl(); + + GstElement *videoSink() override; + void setVideoSink(GstElement *) override; + + QWidget *videoWidget() override; + + void stopRenderer() override; + + Qt::AspectRatioMode aspectRatioMode() const override; + void setAspectRatioMode(Qt::AspectRatioMode mode) override; + + bool isFullScreen() const override; + void setFullScreen(bool fullScreen) override; + + int brightness() const override; + void setBrightness(int brightness) override; + + int contrast() const override; + void setContrast(int contrast) override; + + int hue() const override; + void setHue(int hue) override; + + int saturation() const override; + void setSaturation(int saturation) override; + + bool eventFilter(QObject *object, QEvent *event) override; + +signals: + void sinkChanged(); + void readyChanged(bool); + +private Q_SLOTS: + void onOverlayActiveChanged(); + void onNativeVideoSizeChanged(); + +private: + void createVideoWidget(); + void updateWidgetAttributes(); + + bool processSyncMessage(const QGstreamerMessage &message) override; + bool processBusMessage(const QGstreamerMessage &message) override; + + QGstreamerVideoOverlay m_videoOverlay; + QGstreamerVideoWidget *m_widget = nullptr; + bool m_stopped = false; + WId m_windowId = 0; + bool m_fullScreen = false; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERVIDEOWIDGET_H diff --git a/src/multimediawidgets/multimediawidgets.pro b/src/multimediawidgets/multimediawidgets.pro index fcc908d14..b8204ef07 100644 --- a/src/multimediawidgets/multimediawidgets.pro +++ b/src/multimediawidgets/multimediawidgets.pro @@ -26,6 +26,7 @@ qtConfig(graphicsview) { PUBLIC_HEADERS += qgraphicsvideoitem.h } +qtConfig(gstreamer):include(gstreamer/gstreamer.pri) HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS diff --git a/src/plugins/gstreamer/audiodecoder/CMakeLists.txt b/src/plugins/gstreamer/audiodecoder/CMakeLists.txt index 7aba3e375..019cd5a48 100644 --- a/src/plugins/gstreamer/audiodecoder/CMakeLists.txt +++ b/src/plugins/gstreamer/audiodecoder/CMakeLists.txt @@ -20,7 +20,7 @@ qt_internal_add_plugin(QGstreamerAudioDecoderServicePlugin Qt::MultimediaPrivate Qt::Network gstreamer - multimediagsttoolsPrivate + Qt::MultimediaPrivate ) #### Keys ignored in scope 1:.:.:audiodecoder.pro:: diff --git a/src/plugins/gstreamer/camerabin/CMakeLists.txt b/src/plugins/gstreamer/camerabin/CMakeLists.txt index 9584979b3..2ef2f7572 100644 --- a/src/plugins/gstreamer/camerabin/CMakeLists.txt +++ b/src/plugins/gstreamer/camerabin/CMakeLists.txt @@ -34,7 +34,7 @@ qt_internal_add_plugin(CameraBinServicePlugin Qt::MultimediaPrivate Qt::Network gstreamer - multimediagsttoolsPrivate + Qt::MultimediaPrivate ) #### Keys ignored in scope 1:.:.:camerabin.pro:: diff --git a/src/plugins/gstreamer/camerabin/camerabin.pro b/src/plugins/gstreamer/camerabin/camerabin.pro index 728608074..0623dbc2b 100644 --- a/src/plugins/gstreamer/camerabin/camerabin.pro +++ b/src/plugins/gstreamer/camerabin/camerabin.pro @@ -51,6 +51,8 @@ qtConfig(gstreamer_photography) { DEFINES += GST_USE_UNSTABLE_API #prevents warnings because of unstable photography API } +qtConfig(gstreamer_gl): QMAKE_USE += gstreamer_gl + qtConfig(linux_v4l) { HEADERS += \ $$PWD/camerabinv4limageprocessing.h diff --git a/src/plugins/gstreamer/common.pri b/src/plugins/gstreamer/common.pri index c81a65c05..8e5c381ae 100644 --- a/src/plugins/gstreamer/common.pri +++ b/src/plugins/gstreamer/common.pri @@ -1,4 +1,4 @@ -QT += core-private multimedia-private multimediagsttools-private network +QT += core-private multimedia-private network qtHaveModule(widgets) { QT += widgets multimediawidgets-private diff --git a/src/plugins/gstreamer/mediacapture/CMakeLists.txt b/src/plugins/gstreamer/mediacapture/CMakeLists.txt index a52c2f7b9..763d9a3c9 100644 --- a/src/plugins/gstreamer/mediacapture/CMakeLists.txt +++ b/src/plugins/gstreamer/mediacapture/CMakeLists.txt @@ -28,7 +28,7 @@ qt_internal_add_plugin(QGstreamerCaptureServicePlugin Qt::MultimediaPrivate Qt::Network gstreamer - multimediagsttoolsPrivate + Qt::MultimediaPrivate ) ## Scopes: diff --git a/src/plugins/gstreamer/mediaplayer/CMakeLists.txt b/src/plugins/gstreamer/mediaplayer/CMakeLists.txt index 7b3d8ade2..5bbb26bb9 100644 --- a/src/plugins/gstreamer/mediaplayer/CMakeLists.txt +++ b/src/plugins/gstreamer/mediaplayer/CMakeLists.txt @@ -21,7 +21,7 @@ qt_internal_add_plugin(QGstreamerPlayerServicePlugin Qt::MultimediaPrivate Qt::Network gstreamer - multimediagsttoolsPrivate + Qt::MultimediaPrivate ) #### Keys ignored in scope 1:.:.:mediaplayer.pro:: diff --git a/src/plugins/videonode/imx6/CMakeLists.txt b/src/plugins/videonode/imx6/CMakeLists.txt index 7f61a45fc..412795e9c 100644 --- a/src/plugins/videonode/imx6/CMakeLists.txt +++ b/src/plugins/videonode/imx6/CMakeLists.txt @@ -18,7 +18,6 @@ qt_internal_add_plugin(QSGVivanteVideoNodeFactory Qt::MultimediaPrivate Qt::MultimediaQuickPrivate gstreamer - multimediagsttoolsPrivate ) # Resources: diff --git a/src/plugins/videonode/imx6/imx6.pro b/src/plugins/videonode/imx6/imx6.pro index 4b2a2e720..42769a8fe 100644 --- a/src/plugins/videonode/imx6/imx6.pro +++ b/src/plugins/videonode/imx6/imx6.pro @@ -1,6 +1,6 @@ TARGET = imx6vivantevideonode -QT += multimedia-private qtmultimediaquicktools-private multimediagsttools-private +QT += multimedia-private qtmultimediaquicktools-private QMAKE_USE += gstreamer diff --git a/src/src.pro b/src/src.pro index 1dc1015c6..c1ed05164 100644 --- a/src/src.pro +++ b/src/src.pro @@ -6,9 +6,6 @@ include($$OUT_PWD/multimedia/qtmultimedia-config.pri) QT_FOR_CONFIG += multimedia-private # Everything else depends on multimedia -src_qgsttools.subdir = gsttools -src_qgsttools.depends = multimedia - src_qtmmwidgets.subdir = multimediawidgets src_qtmmwidgets.depends = multimedia @@ -37,16 +34,6 @@ qtHaveModule(widgets) { # If widgets is around, plugins depends on widgets too (imports does not) src_plugins.depends += src_qtmmwidgets - - # same with qgsttools - src_qgsttools.depends += src_qtmmwidgets -} - -qtConfig(gstreamer) { - SUBDIRS += src_qgsttools - - # If gstreamer is present, then plugins should depend on it - src_plugins.depends += src_qgsttools } SUBDIRS += src_plugins -- cgit v1.2.3