diff --git a/.gitignore b/.gitignore index ff4610d..00ed35e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /qtmultimedia-opensource-src-5.3.2.tar.xz -/qtmultimedia-opensource-src-5.4.0-beta.tar.xz -/qtmultimedia-opensource-src-5.4.0-20141118.gst1.tar.xz +/qtmultimedia-opensource-src-5.4.0.tar.xz diff --git a/0006-GStreamer-port-to-1.0.patch b/0006-GStreamer-port-to-1.0.patch new file mode 100644 index 0000000..6ff377e --- /dev/null +++ b/0006-GStreamer-port-to-1.0.patch @@ -0,0 +1,7648 @@ +From 108dda7a90bd0f0337358b0db47ae55acd16dea6 Mon Sep 17 00:00:00 2001 +From: Yoann Lopes +Date: Thu, 20 Nov 2014 17:54:18 +0100 +Subject: [PATCH 06/12] GStreamer: port to 1.0. + +0.10 is still used by default. +To enable GStreamer 1.0, pass GST_VERSION=1.0 to qmake +for qtmultimedia.pro. + +Contributions from: +Andrew den Exter +Ilya Smelykh +Jim Hodapp +Sergio Schvezov + +Change-Id: I72a46d1170a8794a149bdb5e20767afcc5b7587c +Reviewed-by: Andrew den Exter +--- + config.tests/gstreamer/gstreamer.pro | 11 +- + config.tests/gstreamer_appsrc/gstreamer_appsrc.pro | 13 +- + .../gstreamer_encodingprofiles.pro | 13 +- + .../gstreamer_photography.pro | 15 +- + .../declarative-camera/declarative-camera.qml | 2 +- + qtmultimedia.pro | 26 +- + src/gsttools/gsttools.pro | 72 +- + src/gsttools/qgstappsrc.cpp | 29 +- + src/gsttools/qgstcodecsinfo.cpp | 8 +- + src/gsttools/qgstreameraudioprobecontrol.cpp | 47 +- + src/gsttools/qgstreamerbufferprobe.cpp | 174 +++++ + src/gsttools/qgstreamerbushelper.cpp | 8 + + src/gsttools/qgstreamermirtexturerenderer.cpp | 351 +++++++++ + src/gsttools/qgstreamervideoprobecontrol.cpp | 58 +- + src/gsttools/qgstreamervideorenderer.cpp | 3 +- + src/gsttools/qgstreamervideowidget.cpp | 29 +- + src/gsttools/qgstreamervideowindow.cpp | 105 ++- + src/gsttools/qgstutils.cpp | 785 ++++++++++++++++++++- + src/gsttools/qgstvideobuffer.cpp | 70 +- + src/gsttools/qgstvideorendererplugin.cpp | 53 ++ + src/gsttools/qgstvideorenderersink.cpp | 605 ++++++++++++++++ + src/gsttools/qvideosurfacegstsink.cpp | 232 +----- + src/multimedia/gsttools_headers/qgstappsrc_p.h | 3 + + .../qgstreameraudioprobecontrol_p.h | 13 +- + .../gsttools_headers/qgstreamerbufferprobe_p.h | 86 +++ + .../qgstreamermirtexturerenderer_p.h | 102 +++ + .../qgstreamervideoprobecontrol_p.h | 23 +- + .../gsttools_headers/qgstreamervideowindow_p.h | 9 +- + src/multimedia/gsttools_headers/qgstutils_p.h | 55 +- + .../gsttools_headers/qgstvideobuffer_p.h | 23 +- + .../gsttools_headers/qgstvideorendererplugin_p.h | 111 +++ + .../gsttools_headers/qgstvideorenderersink_p.h | 183 +++++ + .../gsttools_headers/qvideosurfacegstsink_p.h | 19 +- + src/multimedia/multimedia.pro | 2 + + .../qgstreameraudiodecoderserviceplugin.cpp | 89 +-- + .../audiodecoder/qgstreameraudiodecodersession.cpp | 36 +- + .../audiodecoder/qgstreameraudiodecodersession.h | 2 +- + src/plugins/gstreamer/camerabin/camerabin.pro | 2 +- + .../gstreamer/camerabin/camerabincontainer.cpp | 2 +- + .../gstreamer/camerabin/camerabincontrol.cpp | 7 +- + .../gstreamer/camerabin/camerabinexposure.cpp | 8 +- + src/plugins/gstreamer/camerabin/camerabinflash.cpp | 8 +- + src/plugins/gstreamer/camerabin/camerabinfocus.cpp | 15 +- + .../gstreamer/camerabin/camerabinimagecapture.cpp | 146 ++-- + .../gstreamer/camerabin/camerabinimagecapture.h | 50 +- + .../gstreamer/camerabin/camerabinimageencoder.cpp | 1 - + .../camerabin/camerabinimageprocessing.cpp | 8 +- + .../gstreamer/camerabin/camerabinimageprocessing.h | 7 +- + .../gstreamer/camerabin/camerabinmetadata.cpp | 5 +- + .../gstreamer/camerabin/camerabinrecorder.cpp | 9 +- + .../gstreamer/camerabin/camerabinservice.cpp | 7 +- + .../gstreamer/camerabin/camerabinsession.cpp | 339 ++++----- + src/plugins/gstreamer/common.pri | 22 +- + src/plugins/gstreamer/gstreamer.pro | 4 +- + .../gstreamer/mediacapture/mediacapturecamera.json | 2 +- + .../mediacapture/qgstreameraudioencode.cpp | 3 +- + .../mediacapture/qgstreamercaptureservice.cpp | 58 +- + .../mediacapture/qgstreamercaptureservice.h | 3 + + .../qgstreamercaptureserviceplugin.cpp | 90 +-- + .../mediacapture/qgstreamercapturesession.cpp | 285 +++----- + .../mediacapture/qgstreamercapturesession.h | 19 +- + .../mediacapture/qgstreamervideoencode.cpp | 39 +- + src/plugins/gstreamer/mediaplayer/mediaplayer.pro | 1 - + .../mediaplayer/qgstreamerplayercontrol.cpp | 1 - + .../mediaplayer/qgstreamerplayerservice.cpp | 65 +- + .../mediaplayer/qgstreamerplayerservice.h | 5 + + .../mediaplayer/qgstreamerplayerserviceplugin.cpp | 88 +-- + .../mediaplayer/qgstreamerplayersession.cpp | 238 ++++--- + .../mediaplayer/qgstreamerplayersession.h | 19 +- + .../qcamerabackend/tst_qcamerabackend.cpp | 10 +- + .../tst_qmediaplayerbackend.cpp | 4 +- + 71 files changed, 3661 insertions(+), 1374 deletions(-) + create mode 100644 src/gsttools/qgstreamerbufferprobe.cpp + create mode 100644 src/gsttools/qgstreamermirtexturerenderer.cpp + create mode 100644 src/gsttools/qgstvideorendererplugin.cpp + create mode 100644 src/gsttools/qgstvideorenderersink.cpp + create mode 100644 src/multimedia/gsttools_headers/qgstreamerbufferprobe_p.h + create mode 100644 src/multimedia/gsttools_headers/qgstreamermirtexturerenderer_p.h + create mode 100644 src/multimedia/gsttools_headers/qgstvideorendererplugin_p.h + create mode 100644 src/multimedia/gsttools_headers/qgstvideorenderersink_p.h + +diff --git a/config.tests/gstreamer/gstreamer.pro b/config.tests/gstreamer/gstreamer.pro +index 02a7e34..6b9843a 100644 +--- a/config.tests/gstreamer/gstreamer.pro ++++ b/config.tests/gstreamer/gstreamer.pro +@@ -3,11 +3,10 @@ SOURCES += main.cpp + CONFIG += link_pkgconfig + + PKGCONFIG += \ +- gstreamer-0.10 \ +- gstreamer-base-0.10 \ +- gstreamer-interfaces-0.10 \ +- gstreamer-audio-0.10 \ +- gstreamer-video-0.10 \ +- gstreamer-pbutils-0.10 ++ gstreamer-$$GST_VERSION \ ++ gstreamer-base-$$GST_VERSION \ ++ gstreamer-audio-$$GST_VERSION \ ++ gstreamer-video-$$GST_VERSION \ ++ gstreamer-pbutils-$$GST_VERSION + + +diff --git a/config.tests/gstreamer_appsrc/gstreamer_appsrc.pro b/config.tests/gstreamer_appsrc/gstreamer_appsrc.pro +index 9f61703..0f3ca2b 100644 +--- a/config.tests/gstreamer_appsrc/gstreamer_appsrc.pro ++++ b/config.tests/gstreamer_appsrc/gstreamer_appsrc.pro +@@ -3,11 +3,8 @@ SOURCES += main.cpp + CONFIG += link_pkgconfig + + PKGCONFIG += \ +- gstreamer-0.10 \ +- gstreamer-base-0.10 \ +- gstreamer-interfaces-0.10 \ +- gstreamer-audio-0.10 \ +- gstreamer-video-0.10 \ +- gstreamer-app-0.10 +- +- ++ gstreamer-$$GST_VERSION \ ++ gstreamer-base-$$GST_VERSION \ ++ gstreamer-audio-$$GST_VERSION \ ++ gstreamer-video-$$GST_VERSION \ ++ gstreamer-pbutils-$$GST_VERSION +diff --git a/config.tests/gstreamer_encodingprofiles/gstreamer_encodingprofiles.pro b/config.tests/gstreamer_encodingprofiles/gstreamer_encodingprofiles.pro +index 7e8a9e7..fad40b0 100644 +--- a/config.tests/gstreamer_encodingprofiles/gstreamer_encodingprofiles.pro ++++ b/config.tests/gstreamer_encodingprofiles/gstreamer_encodingprofiles.pro +@@ -2,11 +2,10 @@ SOURCES += main.cpp + + CONFIG += link_pkgconfig + +-PKGCONFIG += \ +- gstreamer-0.10 \ +- gstreamer-base-0.10 \ +- gstreamer-interfaces-0.10 \ +- gstreamer-audio-0.10 \ +- gstreamer-video-0.10 \ +- gstreamer-pbutils-0.10 + ++PKGCONFIG += \ ++ gstreamer-$$GST_VERSION \ ++ gstreamer-base-$$GST_VERSION \ ++ gstreamer-audio-$$GST_VERSION \ ++ gstreamer-video-$$GST_VERSION \ ++ gstreamer-pbutils-$$GST_VERSION +diff --git a/config.tests/gstreamer_photography/gstreamer_photography.pro b/config.tests/gstreamer_photography/gstreamer_photography.pro +index 6b530cb..975991f 100644 +--- a/config.tests/gstreamer_photography/gstreamer_photography.pro ++++ b/config.tests/gstreamer_photography/gstreamer_photography.pro +@@ -3,12 +3,11 @@ SOURCES += main.cpp + CONFIG += link_pkgconfig + + PKGCONFIG += \ +- gstreamer-0.10 \ +- gstreamer-base-0.10 \ +- gstreamer-interfaces-0.10 \ +- gstreamer-audio-0.10 \ +- gstreamer-video-0.10 \ +- gstreamer-pbutils-0.10 +- +-LIBS += -lgstphotography-0.10 ++ gstreamer-$$GST_VERSION \ ++ gstreamer-base-$$GST_VERSION \ ++ gstreamer-audio-$$GST_VERSION \ ++ gstreamer-video-$$GST_VERSION \ ++ gstreamer-pbutils-$$GST_VERSION ++ ++LIBS += -lgstphotography-$$GST_VERSION + +diff --git a/examples/multimedia/declarative-camera/declarative-camera.qml b/examples/multimedia/declarative-camera/declarative-camera.qml +index 751b38d..251df34 100644 +--- a/examples/multimedia/declarative-camera/declarative-camera.qml ++++ b/examples/multimedia/declarative-camera/declarative-camera.qml +@@ -96,7 +96,7 @@ Rectangle { + + videoRecorder { + resolution: "640x480" +- frameRate: 15 ++ frameRate: 30 + } + } + +diff --git a/qtmultimedia.pro b/qtmultimedia.pro +index 3cec526..84f2548 100644 +--- a/qtmultimedia.pro ++++ b/qtmultimedia.pro +@@ -17,11 +17,27 @@ win32 { + } else { + qtCompileTest(alsa) + qtCompileTest(pulseaudio) +- qtCompileTest(gstreamer) { +- qtCompileTest(gstreamer_photography) +- qtCompileTest(gstreamer_encodingprofiles) +- qtCompileTest(gstreamer_appsrc) +- qtCompileTest(linux_v4l) ++ !done_config_gstreamer { ++ gstver=0.10 ++ !isEmpty(GST_VERSION): gstver=$$GST_VERSION ++ cache(GST_VERSION, set, gstver); ++ qtCompileTest(gstreamer) { ++ qtCompileTest(gstreamer_photography) ++ qtCompileTest(gstreamer_encodingprofiles) ++ qtCompileTest(gstreamer_appsrc) ++ qtCompileTest(linux_v4l) ++ } else { ++ gstver=1.0 ++ cache(GST_VERSION, set, gstver); ++ # Force a re-run of the test ++ CONFIG -= done_config_gstreamer ++ qtCompileTest(gstreamer) { ++ qtCompileTest(gstreamer_photography) ++ qtCompileTest(gstreamer_encodingprofiles) ++ qtCompileTest(gstreamer_appsrc) ++ qtCompileTest(linux_v4l) ++ } ++ } + } + qtCompileTest(resourcepolicy) + qtCompileTest(gpu_vivante) +diff --git a/src/gsttools/gsttools.pro b/src/gsttools/gsttools.pro +index 7c809a7..7c41f1a 100644 +--- a/src/gsttools/gsttools.pro ++++ b/src/gsttools/gsttools.pro +@@ -2,6 +2,7 @@ TEMPLATE = lib + + TARGET = qgsttools_p + QPRO_PWD = $$PWD ++ + QT = core-private multimedia-private gui-private + + !static:DEFINES += QT_MAKEDLL +@@ -15,15 +16,17 @@ LIBS_PRIVATE += \ + + CONFIG += link_pkgconfig + +-PKGCONFIG_PRIVATE += \ +- gstreamer-0.10 \ +- gstreamer-base-0.10 \ +- gstreamer-interfaces-0.10 \ +- gstreamer-audio-0.10 \ +- gstreamer-video-0.10 \ +- gstreamer-pbutils-0.10 ++PKGCONFIG += \ ++ gstreamer-$$GST_VERSION \ ++ gstreamer-base-$$GST_VERSION \ ++ gstreamer-audio-$$GST_VERSION \ ++ gstreamer-video-$$GST_VERSION \ ++ gstreamer-pbutils-$$GST_VERSION + +-maemo*: PKGCONFIG_PRIVATE +=gstreamer-plugins-bad-0.10 ++equals(GST_VERSION,"0.10") { ++ PKGCONFIG_PRIVATE += gstreamer-interfaces-0.10 ++ maemo*: PKGCONFIG_PRIVATE +=gstreamer-plugins-bad-0.10 ++} + + config_resourcepolicy { + DEFINES += HAVE_RESOURCE_POLICY +@@ -33,38 +36,36 @@ config_resourcepolicy { + # Header files must go inside source directory of a module + # to be installed by syncqt. + INCLUDEPATH += ../multimedia/gsttools_headers/ ++INCLUDEPATH += ../plugins/gstreamer/mediaplayer/ + VPATH += ../multimedia/gsttools_headers/ + + PRIVATE_HEADERS += \ +- qgstbufferpoolinterface_p.h \ + qgstreamerbushelper_p.h \ + qgstreamermessage_p.h \ + qgstutils_p.h \ + qgstvideobuffer_p.h \ + qvideosurfacegstsink_p.h \ ++ qgstreamerbufferprobe_p.h \ + qgstreamervideorendererinterface_p.h \ + qgstreameraudioinputselector_p.h \ + qgstreamervideorenderer_p.h \ + qgstreamervideoinputdevicecontrol_p.h \ +- gstvideoconnector_p.h \ + qgstcodecsinfo_p.h \ + qgstreamervideoprobecontrol_p.h \ + qgstreameraudioprobecontrol_p.h \ + qgstreamervideowindow_p.h + + SOURCES += \ +- qgstbufferpoolinterface.cpp \ + qgstreamerbushelper.cpp \ + qgstreamermessage.cpp \ + qgstutils.cpp \ + qgstvideobuffer.cpp \ +- qvideosurfacegstsink.cpp \ ++ qgstreamerbufferprobe.cpp \ + qgstreamervideorendererinterface.cpp \ + qgstreameraudioinputselector.cpp \ + qgstreamervideorenderer.cpp \ + qgstreamervideoinputdevicecontrol.cpp \ + qgstcodecsinfo.cpp \ +- gstvideoconnector.c \ + qgstreamervideoprobecontrol.cpp \ + qgstreameraudioprobecontrol.cpp \ + qgstreamervideowindow.cpp +@@ -79,25 +80,54 @@ qtHaveModule(widgets) { + qgstreamervideowidget.cpp + } + +-maemo6 { +- PKGCONFIG_PRIVATE += qmsystem2 ++equals(GST_VERSION,"0.10") { ++ PRIVATE_HEADERS += \ ++ qgstbufferpoolinterface_p.h \ ++ gstvideoconnector_p.h \ ++ ++ SOURCES += \ ++ qgstbufferpoolinterface.cpp \ ++ qvideosurfacegstsink.cpp \ ++ gstvideoconnector.c ++ ++ maemo6 { ++ PKGCONFIG_PRIVATE += qmsystem2 ++ ++ contains(QT_CONFIG, opengles2):qtHaveModule(widgets) { ++ PRIVATE_HEADERS += qgstreamergltexturerenderer_p.h ++ SOURCES += qgstreamergltexturerenderer.cpp ++ QT += opengl ++ LIBS_PRIVATE += -lEGL -lgstmeegointerfaces-0.10 ++ } ++ } ++} else { ++ PRIVATE_HEADERS += \ ++ qgstvideorendererplugin_p.h \ ++ qgstvideorenderersink_p.h ++ ++ SOURCES += \ ++ qgstvideorendererplugin.cpp \ ++ qgstvideorenderersink.cpp ++} + ++mir: { + contains(QT_CONFIG, opengles2):qtHaveModule(widgets) { +- PRIVATE_HEADERS += qgstreamergltexturerenderer_p.h +- SOURCES += qgstreamergltexturerenderer.cpp +- QT += opengl +- LIBS_PRIVATE += -lEGL -lgstmeegointerfaces-0.10 ++ PRIVATE_HEADERS += qgstreamermirtexturerenderer_p.h ++ SOURCES += qgstreamermirtexturerenderer.cpp ++ QT += opengl quick ++ LIBS += -lEGL + } ++ DEFINES += HAVE_MIR + } + + config_gstreamer_appsrc { +- PKGCONFIG_PRIVATE += gstreamer-app-0.10 ++ PKGCONFIG_PRIVATE += gstreamer-app-$$GST_VERSION + PRIVATE_HEADERS += qgstappsrc_p.h + SOURCES += qgstappsrc.cpp + + DEFINES += HAVE_GST_APPSRC + +- LIBS_PRIVATE += -lgstapp-0.10 ++ LIBS_PRIVATE += -lgstapp-$$GST_VERSION + } + + config_linux_v4l: DEFINES += USE_V4L +diff --git a/src/gsttools/qgstappsrc.cpp b/src/gsttools/qgstappsrc.cpp +index 561a96f..178e118 100644 +--- a/src/gsttools/qgstappsrc.cpp ++++ b/src/gsttools/qgstappsrc.cpp +@@ -147,23 +147,44 @@ void QGstAppSrc::pushDataToAppSrc() + size = qMin(m_stream->bytesAvailable(), (qint64)m_dataRequestSize); + + if (size) { +- void *data = g_malloc(size); +- GstBuffer* buffer = gst_app_buffer_new(data, size, g_free, data); ++ GstBuffer* buffer = gst_buffer_new_and_alloc(size); ++ ++#if GST_CHECK_VERSION(1,0,0) ++ GstMapInfo mapInfo; ++ gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE); ++ void* bufferData = mapInfo.data; ++#else ++ void* bufferData = GST_BUFFER_DATA(buffer); ++#endif ++ + buffer->offset = m_stream->pos(); +- qint64 bytesRead = m_stream->read((char*)GST_BUFFER_DATA(buffer), size); ++ qint64 bytesRead = m_stream->read((char*)bufferData, size); + buffer->offset_end = buffer->offset + bytesRead - 1; + ++#if GST_CHECK_VERSION(1,0,0) ++ gst_buffer_unmap(buffer, &mapInfo); ++#endif ++ + 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"; ++#if GST_CHECK_VERSION(1,0,0) ++ } else if (ret == GST_FLOW_FLUSHING) { ++ qWarning()<<"appsrc: push buffer wrong state"; ++ } ++#else + } else if (ret == GST_FLOW_WRONG_STATE) { + qWarning()<<"appsrc: push buffer wrong state"; +- } else if (ret == GST_FLOW_RESEND) { ++ } ++#endif ++#if GST_VERSION_MAJOR < 1 ++ else if (ret == GST_FLOW_RESEND) { + qWarning()<<"appsrc: push buffer resend"; + } ++#endif + } + } else { + sendEOS(); +diff --git a/src/gsttools/qgstcodecsinfo.cpp b/src/gsttools/qgstcodecsinfo.cpp +index f584fbe..888722a 100644 +--- a/src/gsttools/qgstcodecsinfo.cpp ++++ b/src/gsttools/qgstcodecsinfo.cpp +@@ -32,7 +32,7 @@ + ****************************************************************************/ + + #include "qgstcodecsinfo_p.h" +- ++#include "qgstutils_p.h" + #include + + #ifdef QMEDIA_GSTREAMER_CAMERABIN +@@ -146,7 +146,7 @@ GstCaps* QGstCodecsInfo::supportedElementCaps(GstElementFactoryListType elementT + if (fakeEncoderMimeTypes.contains(gst_structure_get_name(structure))) + continue; + +- GstStructure *newStructure = gst_structure_new(gst_structure_get_name(structure), NULL); ++ GstStructure *newStructure = qt_gst_structure_new_empty(gst_structure_get_name(structure)); + + //add structure fields to distinguish between formats with similar mime types, + //like audio/mpeg +@@ -166,7 +166,11 @@ GstCaps* QGstCodecsInfo::supportedElementCaps(GstElementFactoryListType elementT + } + } + ++#if GST_CHECK_VERSION(1,0,0) ++ res = ++#endif + gst_caps_merge_structure(res, newStructure); ++ + } + gst_caps_unref(caps); + } +diff --git a/src/gsttools/qgstreameraudioprobecontrol.cpp b/src/gsttools/qgstreameraudioprobecontrol.cpp +index 3baca53..9670d0f 100644 +--- a/src/gsttools/qgstreameraudioprobecontrol.cpp ++++ b/src/gsttools/qgstreameraudioprobecontrol.cpp +@@ -37,32 +37,48 @@ + QGstreamerAudioProbeControl::QGstreamerAudioProbeControl(QObject *parent) + : QMediaAudioProbeControl(parent) + { +- + } + + QGstreamerAudioProbeControl::~QGstreamerAudioProbeControl() + { +- + } + +-void QGstreamerAudioProbeControl::bufferProbed(GstBuffer* buffer) ++void QGstreamerAudioProbeControl::probeCaps(GstCaps *caps) + { +- GstCaps* caps = gst_buffer_get_caps(buffer); +- if (!caps) +- return; +- + QAudioFormat format = QGstUtils::audioFormatForCaps(caps); +- gst_caps_unref(caps); +- if (!format.isValid()) +- return; + +- QAudioBuffer audioBuffer = QAudioBuffer(QByteArray((const char*)buffer->data, buffer->size), format); ++ QMutexLocker locker(&m_bufferMutex); ++ m_format = format; ++} + +- { +- QMutexLocker locker(&m_bufferMutex); +- m_pendingBuffer = audioBuffer; +- QMetaObject::invokeMethod(this, "bufferProbed", Qt::QueuedConnection); ++bool QGstreamerAudioProbeControl::probeBuffer(GstBuffer *buffer) ++{ ++ qint64 position = GST_BUFFER_TIMESTAMP(buffer); ++ position = position >= 0 ++ ? position / G_GINT64_CONSTANT(1000) // microseconds ++ : -1; ++ ++ QByteArray data; ++#if GST_CHECK_VERSION(1,0,0) ++ 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; ++ } ++#else ++ data = QByteArray(reinterpret_cast(buffer->data), buffer->size); ++#endif ++ ++ 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() +@@ -73,6 +89,7 @@ void QGstreamerAudioProbeControl::bufferProbed() + if (!m_pendingBuffer.isValid()) + return; + audioBuffer = m_pendingBuffer; ++ m_pendingBuffer = QAudioBuffer(); + } + emit audioBufferProbed(audioBuffer); + } +diff --git a/src/gsttools/qgstreamerbufferprobe.cpp b/src/gsttools/qgstreamerbufferprobe.cpp +new file mode 100644 +index 0000000..91f8126 +--- /dev/null ++++ b/src/gsttools/qgstreamerbufferprobe.cpp +@@ -0,0 +1,174 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Jolla Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 2.1 requirements ++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3.0 as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 3.0 requirements will be ++** met: http://www.gnu.org/copyleft/gpl.html. ++** ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "qgstreamerbufferprobe_p.h" ++#include "qgstutils_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++QGstreamerBufferProbe::QGstreamerBufferProbe(Flags flags) ++#if GST_CHECK_VERSION(1,0,0) ++ : m_capsProbeId(-1) ++#else ++ : m_caps(0) ++#endif ++ , m_bufferProbeId(-1) ++ , m_flags(flags) ++{ ++} ++ ++QGstreamerBufferProbe::~QGstreamerBufferProbe() ++{ ++#if !GST_CHECK_VERSION(1,0,0) ++ if (m_caps) ++ gst_caps_unref(m_caps); ++#endif ++} ++ ++void QGstreamerBufferProbe::addProbeToPad(GstPad *pad, bool downstream) ++{ ++ if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) { ++ probeCaps(caps); ++ gst_caps_unref(caps); ++ } ++#if GST_CHECK_VERSION(1,0,0) ++ 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, ++ NULL); ++ } ++ if (m_flags & ProbeBuffers) { ++ m_bufferProbeId = gst_pad_add_probe( ++ pad, GST_PAD_PROBE_TYPE_BUFFER, bufferProbe, this, NULL); ++ } ++#else ++ Q_UNUSED(downstream); ++ ++ m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(bufferProbe), this); ++#endif ++} ++ ++void QGstreamerBufferProbe::removeProbeFromPad(GstPad *pad) ++{ ++#if GST_CHECK_VERSION(1,0,0) ++ 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; ++ } ++#else ++ if (m_bufferProbeId != -1) { ++ gst_pad_remove_buffer_probe(pad, m_bufferProbeId); ++ m_bufferProbeId = -1; ++ if (m_caps) { ++ gst_caps_unref(m_caps); ++ m_caps = 0; ++ } ++ } ++#endif ++} ++ ++void QGstreamerBufferProbe::probeCaps(GstCaps *) ++{ ++} ++ ++bool QGstreamerBufferProbe::probeBuffer(GstBuffer *) ++{ ++ return true; ++} ++ ++#if GST_CHECK_VERSION(1,0,0) ++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; ++} ++#else ++gboolean QGstreamerBufferProbe::bufferProbe(GstElement *, GstBuffer *buffer, gpointer user_data) ++{ ++ QGstreamerBufferProbe * const control = static_cast(user_data); ++ ++ if (control->m_flags & ProbeCaps) { ++ GstCaps *caps = gst_buffer_get_caps(buffer); ++ if (caps && (!control->m_caps || !gst_caps_is_equal(control->m_caps, caps))) { ++ qSwap(caps, control->m_caps); ++ control->probeCaps(control->m_caps); ++ } ++ if (caps) ++ gst_caps_unref(caps); ++ } ++ ++ if (control->m_flags & ProbeBuffers) { ++ return control->probeBuffer(buffer) ? TRUE : FALSE; ++ } else { ++ return TRUE; ++ } ++} ++#endif ++ ++QT_END_NAMESPACE +diff --git a/src/gsttools/qgstreamerbushelper.cpp b/src/gsttools/qgstreamerbushelper.cpp +index 84eda46..eb1fc36 100644 +--- a/src/gsttools/qgstreamerbushelper.cpp ++++ b/src/gsttools/qgstreamerbushelper.cpp +@@ -154,13 +154,21 @@ QGstreamerBusHelper::QGstreamerBusHelper(GstBus* bus, QObject* parent): + QObject(parent) + { + d = new QGstreamerBusHelperPrivate(this, bus); ++#if GST_CHECK_VERSION(1,0,0) ++ gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, d, 0); ++#else + gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, d); ++#endif + gst_object_ref(GST_OBJECT(bus)); + } + + QGstreamerBusHelper::~QGstreamerBusHelper() + { ++#if GST_CHECK_VERSION(1,0,0) ++ gst_bus_set_sync_handler(d->bus(), 0, 0, 0); ++#else + gst_bus_set_sync_handler(d->bus(),0,0); ++#endif + gst_object_unref(GST_OBJECT(d->bus())); + } + +diff --git a/src/gsttools/qgstreamermirtexturerenderer.cpp b/src/gsttools/qgstreamermirtexturerenderer.cpp +new file mode 100644 +index 0000000..39e0db7 +--- /dev/null ++++ b/src/gsttools/qgstreamermirtexturerenderer.cpp +@@ -0,0 +1,351 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Canonical Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** This file is part of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL21$ ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free ++** Software Foundation and appearing in the file LICENSE.LGPLv21 and ++** LICENSE.LGPLv3 included in the packaging of this file. Please review the ++** following information to ensure the GNU Lesser General Public License ++** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ++** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include "qgstreamermirtexturerenderer_p.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static QGstreamerMirTextureRenderer *rendererInstance = NULL; ++ ++class QGstreamerMirTextureBuffer : public QAbstractVideoBuffer ++{ ++public: ++ QGstreamerMirTextureBuffer(GLuint textureId) : ++ QAbstractVideoBuffer(QAbstractVideoBuffer::GLTextureHandle), ++ m_textureId(textureId) ++ { ++ } ++ ++ MapMode mapMode() const { return NotMapped; } ++ ++ uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) ++ { ++ qDebug() << Q_FUNC_INFO; ++ Q_UNUSED(mode); ++ Q_UNUSED(numBytes); ++ Q_UNUSED(bytesPerLine); ++ ++ return NULL; ++ } ++ ++ void unmap() { qDebug() << Q_FUNC_INFO; } ++ ++ QVariant handle() const { return QVariant::fromValue(m_textureId); } ++ ++ GLuint textureId() { return m_textureId; } ++ ++private: ++ GLuint m_textureId; ++}; ++ ++QGstreamerMirTextureRenderer::QGstreamerMirTextureRenderer(QObject *parent ++ , const QGstreamerPlayerSession *playerSession) ++ : QVideoRendererControl(0), m_videoSink(0), m_surface(0), ++ m_glSurface(0), ++ m_context(0), ++ m_glContext(0), ++ m_textureId(0), ++ m_offscreenSurface(0), ++ m_textureBuffer(0) ++{ ++ Q_UNUSED(parent); ++ setPlayerSession(playerSession); ++} ++ ++QGstreamerMirTextureRenderer::~QGstreamerMirTextureRenderer() ++{ ++ if (m_videoSink) ++ gst_object_unref(GST_OBJECT(m_videoSink)); ++ ++ delete m_glContext; ++ delete m_offscreenSurface; ++} ++ ++GstElement *QGstreamerMirTextureRenderer::videoSink() ++{ ++ qDebug() << Q_FUNC_INFO; ++ ++ // FIXME: Ugly hack until I figure out why passing this segfaults in the g_signal handler ++ rendererInstance = const_cast(this); ++ ++ if (!m_videoSink && m_surface) { ++ qDebug() << Q_FUNC_INFO << ": using mirsink, (this: " << this << ")"; ++ ++ m_videoSink = gst_element_factory_make("mirsink", "video-output"); ++ ++ connect(QGuiApplication::instance(), SIGNAL(focusWindowChanged(QWindow*)), ++ this, SLOT(handleFocusWindowChanged(QWindow*)), Qt::QueuedConnection); ++ ++ g_signal_connect(G_OBJECT(m_videoSink), "frame-ready", G_CALLBACK(handleFrameReady), ++ (gpointer)this); ++ } ++ ++ if (m_videoSink) { ++ gst_object_ref_sink(GST_OBJECT(m_videoSink)); ++ ++ GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); ++ gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, ++ padBufferProbe, this, NULL); ++ } ++ ++ return m_videoSink; ++} ++ ++QWindow *QGstreamerMirTextureRenderer::createOffscreenWindow(const QSurfaceFormat &format) ++{ ++ QWindow *w = new QWindow(); ++ w->setSurfaceType(QWindow::OpenGLSurface); ++ w->setFormat(format); ++ w->setGeometry(0, 0, 1, 1); ++ w->setFlags(w->flags() | Qt::WindowTransparentForInput); ++ w->create(); ++ ++ return w; ++} ++ ++void QGstreamerMirTextureRenderer::handleFrameReady(gpointer userData) ++{ ++ QGstreamerMirTextureRenderer *renderer = reinterpret_cast(userData); ++#if 1 ++ QMutexLocker locker(&rendererInstance->m_mutex); ++ QMetaObject::invokeMethod(rendererInstance, "renderFrame", Qt::QueuedConnection); ++#else ++ // FIXME! ++ //QMutexLocker locker(&renderer->m_mutex); ++ QMetaObject::invokeMethod(renderer, "renderFrame", Qt::QueuedConnection); ++#endif ++} ++ ++void QGstreamerMirTextureRenderer::renderFrame() ++{ ++ //qDebug() << Q_FUNC_INFO; ++ ++ if (m_context) ++ m_context->makeCurrent(); ++ ++ GstState pendingState = GST_STATE_NULL; ++ GstState newState = GST_STATE_NULL; ++ // Don't block and return immediately: ++ GstStateChangeReturn ret = gst_element_get_state(m_videoSink, &newState, ++ &pendingState, 0); ++ if (ret == GST_STATE_CHANGE_FAILURE || newState == GST_STATE_NULL|| ++ pendingState == GST_STATE_NULL) { ++ qWarning() << "Invalid state change for renderer, aborting"; ++ stopRenderer(); ++ return; ++ } ++ ++ if (!m_surface->isActive()) { ++ qDebug() << "m_surface is not active"; ++ GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); ++ GstCaps *caps = gst_pad_get_current_caps(pad); ++ ++ if (caps) { ++ // Get the native video size from the video sink ++ QSize newNativeSize = QGstUtils::capsCorrectedResolution(caps); ++ if (m_nativeSize != newNativeSize) { ++ m_nativeSize = newNativeSize; ++ emit nativeSizeChanged(); ++ } ++ gst_caps_unref(caps); ++ } ++ ++ // Start the surface ++ QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle); ++ qDebug() << "m_nativeSize: " << m_nativeSize; ++ qDebug() << "format: " << format; ++ if (!m_surface->start(format)) { ++ qWarning() << Q_FUNC_INFO << ": failed to start the video surface " << format; ++ return; ++ } ++ } ++ ++ QGstreamerMirTextureBuffer *buffer = new QGstreamerMirTextureBuffer(m_textureId); ++ //qDebug() << "frameSize: " << m_surface->surfaceFormat().frameSize(); ++ QVideoFrame frame(buffer, m_surface->surfaceFormat().frameSize(), ++ m_surface->surfaceFormat().pixelFormat()); ++ ++ frame.setMetaData("TextureId", m_textureId); ++ ++ // Display the video frame on the surface: ++ m_surface->present(frame); ++} ++ ++GstPadProbeReturn QGstreamerMirTextureRenderer::padBufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer userData) ++{ ++ Q_UNUSED(pad); ++ Q_UNUSED(info); ++ ++ QGstreamerMirTextureRenderer *control = reinterpret_cast(userData); ++ QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); ++ ++ return GST_PAD_PROBE_REMOVE; ++} ++ ++void QGstreamerMirTextureRenderer::stopRenderer() ++{ ++ if (m_surface) ++ m_surface->stop(); ++} ++ ++QAbstractVideoSurface *QGstreamerMirTextureRenderer::surface() const ++{ ++ return m_surface; ++} ++ ++void QGstreamerMirTextureRenderer::setSurface(QAbstractVideoSurface *surface) ++{ ++ qDebug() << Q_FUNC_INFO; ++ ++ if (m_surface != surface) { ++ qDebug() << "Saving current QGLContext"; ++ m_context = const_cast(QGLContext::currentContext()); ++ ++ if (m_videoSink) ++ gst_object_unref(GST_OBJECT(m_videoSink)); ++ ++ m_videoSink = 0; ++ ++ if (m_surface) { ++ disconnect(m_surface.data(), SIGNAL(supportedFormatsChanged()), ++ this, SLOT(handleFormatChange())); ++ } ++ ++ bool wasReady = isReady(); ++ ++ m_surface = surface; ++ ++ if (m_surface) { ++ connect(m_surface.data(), SIGNAL(supportedFormatsChanged()), ++ this, SLOT(handleFormatChange())); ++ } ++ ++ if (wasReady != isReady()) ++ emit readyChanged(isReady()); ++ ++ emit sinkChanged(); ++ } ++} ++ ++void QGstreamerMirTextureRenderer::setPlayerSession(const QGstreamerPlayerSession *playerSession) ++{ ++ m_playerSession = const_cast(playerSession); ++} ++ ++void QGstreamerMirTextureRenderer::handleFormatChange() ++{ ++ qDebug() << "Supported formats list has changed, reload video output"; ++ ++ if (m_videoSink) ++ gst_object_unref(GST_OBJECT(m_videoSink)); ++ ++ m_videoSink = 0; ++ emit sinkChanged(); ++} ++ ++void QGstreamerMirTextureRenderer::updateNativeVideoSize() ++{ ++ //qDebug() << Q_FUNC_INFO; ++ const QSize oldSize = m_nativeSize; ++ ++ if (m_videoSink) { ++ // Find video native size to update video widget size hint ++ GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); ++ GstCaps *caps = gst_pad_get_current_caps(pad); ++ ++ if (caps) { ++ m_nativeSize = QGstUtils::capsCorrectedResolution(caps); ++ gst_caps_unref(caps); ++ } ++ } else { ++ m_nativeSize = QSize(); ++ } ++ qDebug() << Q_FUNC_INFO << oldSize << m_nativeSize << m_videoSink; ++ ++ if (m_nativeSize != oldSize) ++ emit nativeSizeChanged(); ++} ++ ++void QGstreamerMirTextureRenderer::handleFocusWindowChanged(QWindow *window) ++{ ++ qDebug() << Q_FUNC_INFO; ++ ++ QOpenGLContext *currContext = QOpenGLContext::currentContext(); ++ ++ QQuickWindow *w = dynamic_cast(window); ++ // If we don't have a GL context in the current thread, create one and share it ++ // with the render thread GL context ++ if (!currContext && !m_glContext) { ++ // This emulates the new QOffscreenWindow class with Qt5.1 ++ m_offscreenSurface = createOffscreenWindow(w->openglContext()->surface()->format()); ++ m_offscreenSurface->setParent(window); ++ ++ QOpenGLContext *shareContext = 0; ++ if (m_surface) ++ shareContext = qobject_cast(m_surface->property("GLContext").value()); ++ m_glContext = new QOpenGLContext; ++ m_glContext->setFormat(m_offscreenSurface->requestedFormat()); ++ ++ if (shareContext) ++ m_glContext->setShareContext(shareContext); ++ ++ if (!m_glContext->create()) ++ { ++ qWarning() << "Failed to create new shared context."; ++ return; ++ } ++ } ++ ++ if (m_glContext) ++ m_glContext->makeCurrent(m_offscreenSurface); ++ ++ if (m_textureId == 0) { ++ glGenTextures(1, &m_textureId); ++ qDebug() << "texture_id (handleFocusWindowChanged): " << m_textureId << endl; ++ g_object_set(G_OBJECT(m_videoSink), "texture-id", m_textureId, (char*)NULL); ++ } ++} +diff --git a/src/gsttools/qgstreamervideoprobecontrol.cpp b/src/gsttools/qgstreamervideoprobecontrol.cpp +index a78a9da..71a402d 100644 +--- a/src/gsttools/qgstreamervideoprobecontrol.cpp ++++ b/src/gsttools/qgstreamervideoprobecontrol.cpp +@@ -32,7 +32,8 @@ + ****************************************************************************/ + + #include "qgstreamervideoprobecontrol_p.h" +-#include ++ ++#include "qgstutils_p.h" + #include + + QGstreamerVideoProbeControl::QGstreamerVideoProbeControl(QObject *parent) +@@ -40,12 +41,10 @@ QGstreamerVideoProbeControl::QGstreamerVideoProbeControl(QObject *parent) + , m_flushing(false) + , m_frameProbed(false) + { +- + } + + QGstreamerVideoProbeControl::~QGstreamerVideoProbeControl() + { +- + } + + void QGstreamerVideoProbeControl::startFlushing() +@@ -67,33 +66,49 @@ void QGstreamerVideoProbeControl::stopFlushing() + m_flushing = false; + } + +-void QGstreamerVideoProbeControl::bufferProbed(GstBuffer* buffer) ++void QGstreamerVideoProbeControl::probeCaps(GstCaps *caps) + { +- if (m_flushing) +- return; +- +- GstCaps* caps = gst_buffer_get_caps(buffer); +- if (!caps) +- return; ++#if GST_CHECK_VERSION(1,0,0) ++ GstVideoInfo videoInfo; ++ QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &videoInfo); + ++ QMutexLocker locker(&m_frameMutex); ++ m_videoInfo = videoInfo; ++#else + int bytesPerLine = 0; +- QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine); +- gst_caps_unref(caps); +- if (!format.isValid() || !bytesPerLine) +- return; ++ QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine); ++ ++ QMutexLocker locker(&m_frameMutex); ++ m_bytesPerLine = bytesPerLine; ++#endif ++ m_format = format; ++} ++ ++bool QGstreamerVideoProbeControl::probeBuffer(GstBuffer *buffer) ++{ ++ QMutexLocker locker(&m_frameMutex); ++ ++ if (m_flushing || !m_format.isValid()) ++ return true; + +- QVideoFrame frame = QVideoFrame(new QGstVideoBuffer(buffer, bytesPerLine), +- format.frameSize(), format.pixelFormat()); ++ QVideoFrame frame( ++#if GST_CHECK_VERSION(1,0,0) ++ new QGstVideoBuffer(buffer, m_videoInfo), ++#else ++ new QGstVideoBuffer(buffer, m_bytesPerLine), ++#endif ++ m_format.frameSize(), ++ m_format.pixelFormat()); + +- QVideoSurfaceGstSink::setFrameTimeStamps(&frame, buffer); ++ QGstUtils::setFrameTimeStamps(&frame, buffer); + + m_frameProbed = true; + +- { +- QMutexLocker locker(&m_frameMutex); +- m_pendingFrame = frame; ++ if (!m_pendingFrame.isValid()) + QMetaObject::invokeMethod(this, "frameProbed", Qt::QueuedConnection); +- } ++ m_pendingFrame = frame; ++ ++ return true; + } + + void QGstreamerVideoProbeControl::frameProbed() +@@ -104,6 +119,7 @@ void QGstreamerVideoProbeControl::frameProbed() + if (!m_pendingFrame.isValid()) + return; + frame = m_pendingFrame; ++ m_pendingFrame = QVideoFrame(); + } + emit videoFrameProbed(frame); + } +diff --git a/src/gsttools/qgstreamervideorenderer.cpp b/src/gsttools/qgstreamervideorenderer.cpp +index 2b66f76..804dce9 100644 +--- a/src/gsttools/qgstreamervideorenderer.cpp ++++ b/src/gsttools/qgstreamervideorenderer.cpp +@@ -35,8 +35,7 @@ + #include + #include + #include +- +-#include ++#include + + #include + +diff --git a/src/gsttools/qgstreamervideowidget.cpp b/src/gsttools/qgstreamervideowidget.cpp +index aa2e2a3..1ae57a0 100644 +--- a/src/gsttools/qgstreamervideowidget.cpp ++++ b/src/gsttools/qgstreamervideowidget.cpp +@@ -40,8 +40,13 @@ + #include + + #include ++ ++#if !GST_CHECK_VERSION(1,0,0) + #include + #include ++#else ++#include ++#endif + + QT_BEGIN_NAMESPACE + +@@ -130,8 +135,6 @@ void QGstreamerVideoWidgetControl::createVideoWidget() + m_videoSink = gst_element_factory_make ("ximagesink", NULL); + + qt_gst_object_ref_sink(GST_OBJECT (m_videoSink)); //Take ownership +- +- + } + + GstElement *QGstreamerVideoWidgetControl::videoSink() +@@ -169,9 +172,13 @@ bool QGstreamerVideoWidgetControl::processSyncMessage(const QGstreamerMessage &m + { + GstMessage* gm = message.rawMessage(); + ++#if !GST_CHECK_VERSION(1,0,0) + if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && + gst_structure_has_name(gm->structure, "prepare-xwindow-id")) { +- ++#else ++ if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && ++ gst_structure_has_name(gst_message_get_structure(gm), "prepare-window-handle")) { ++#endif + setOverlay(); + QMetaObject::invokeMethod(this, "updateNativeVideoSize", Qt::QueuedConnection); + return true; +@@ -199,17 +206,24 @@ bool QGstreamerVideoWidgetControl::processBusMessage(const QGstreamerMessage &me + + void QGstreamerVideoWidgetControl::setOverlay() + { ++#if !GST_CHECK_VERSION(1,0,0) + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + } ++#else ++ if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { ++ gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), m_windowId); ++ } ++#endif + } + + void QGstreamerVideoWidgetControl::updateNativeVideoSize() + { + if (m_videoSink) { + //find video native size to update video widget size hint +- GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); +- GstCaps *caps = gst_pad_get_negotiated_caps(pad); ++ GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); ++ GstCaps *caps = qt_gst_pad_get_current_caps(pad); ++ + gst_object_unref(GST_OBJECT(pad)); + + if (caps) { +@@ -225,8 +239,13 @@ void QGstreamerVideoWidgetControl::updateNativeVideoSize() + + void QGstreamerVideoWidgetControl::windowExposed() + { ++#if !GST_CHECK_VERSION(1,0,0) + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); ++#else ++ if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) ++ gst_video_overlay_expose(GST_VIDEO_OVERLAY(m_videoSink)); ++#endif + } + + QWidget *QGstreamerVideoWidgetControl::videoWidget() +diff --git a/src/gsttools/qgstreamervideowindow.cpp b/src/gsttools/qgstreamervideowindow.cpp +index a373dcc..8011349 100644 +--- a/src/gsttools/qgstreamervideowindow.cpp ++++ b/src/gsttools/qgstreamervideowindow.cpp +@@ -37,36 +37,49 @@ + #include + + #include ++ ++#if !GST_CHECK_VERSION(1,0,0) + #include + #include ++#else ++#include ++#endif + + + QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const char *elementName) + : QVideoWindowControl(parent) ++ , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps) + , m_videoSink(0) + , m_windowId(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_fullScreen(false) + , m_colorKey(QColor::Invalid) + { +- if (elementName) ++ if (elementName) { + m_videoSink = gst_element_factory_make(elementName, NULL); +- else ++ } else { + m_videoSink = gst_element_factory_make("xvimagesink", NULL); ++ } + + if (m_videoSink) { + qt_gst_object_ref_sink(GST_OBJECT(m_videoSink)); //Take ownership + +- GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); +- m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); ++ GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); ++ addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } ++ else ++ qDebug() << "No m_videoSink available!"; + } + + QGstreamerVideoWindow::~QGstreamerVideoWindow() + { +- if (m_videoSink) ++ if (m_videoSink) { ++ 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)); ++ } + } + + WId QGstreamerVideoWindow::winId() const +@@ -82,11 +95,15 @@ void QGstreamerVideoWindow::setWinId(WId id) + WId oldId = m_windowId; + + m_windowId = id; +- ++#if GST_CHECK_VERSION(1,0,0) ++ if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { ++ gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), m_windowId); ++ } ++#else + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + } +- ++#endif + if (!oldId) + emit readyChanged(true); + +@@ -97,20 +114,26 @@ void QGstreamerVideoWindow::setWinId(WId id) + bool QGstreamerVideoWindow::processSyncMessage(const QGstreamerMessage &message) + { + GstMessage* gm = message.rawMessage(); ++#if GST_CHECK_VERSION(1,0,0) ++ const GstStructure *s = gst_message_get_structure(gm); ++ if ((GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && ++ gst_structure_has_name(s, "prepare-window-handle") && ++ m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { ++ ++ gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), m_windowId); + ++ return true; ++ } ++#else + if ((GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && + gst_structure_has_name(gm->structure, "prepare-xwindow-id") && + m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + +- GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); +- m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); +- gst_object_unref(GST_OBJECT(pad)); +- + return true; + } +- ++#endif + return false; + } + +@@ -122,7 +145,19 @@ QRect QGstreamerVideoWindow::displayRect() const + void QGstreamerVideoWindow::setDisplayRect(const QRect &rect) + { + m_displayRect = rect; +- ++#if GST_CHECK_VERSION(1,0,0) ++ if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { ++ if (m_displayRect.isEmpty()) ++ gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink), -1, -1, -1, -1); ++ else ++ gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink), ++ m_displayRect.x(), ++ m_displayRect.y(), ++ m_displayRect.width(), ++ m_displayRect.height()); ++ repaint(); ++ } ++#else + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + #if GST_VERSION_MICRO >= 29 + if (m_displayRect.isEmpty()) +@@ -136,6 +171,7 @@ void QGstreamerVideoWindow::setDisplayRect(const QRect &rect) + repaint(); + #endif + } ++#endif + } + + Qt::AspectRatioMode QGstreamerVideoWindow::aspectRatioMode() const +@@ -157,6 +193,16 @@ void QGstreamerVideoWindow::setAspectRatioMode(Qt::AspectRatioMode mode) + + void QGstreamerVideoWindow::repaint() + { ++#if GST_CHECK_VERSION(1,0,0) ++ if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { ++ //don't call gst_x_overlay_expose if the sink is in null state ++ GstState state = GST_STATE_NULL; ++ GstStateChangeReturn res = gst_element_get_state(m_videoSink, &state, NULL, 1000000); ++ if (res != GST_STATE_CHANGE_FAILURE && state != GST_STATE_NULL) { ++ gst_video_overlay_expose(GST_VIDEO_OVERLAY(m_videoSink)); ++ } ++ } ++#else + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + //don't call gst_x_overlay_expose if the sink is in null state + GstState state = GST_STATE_NULL; +@@ -165,6 +211,7 @@ void QGstreamerVideoWindow::repaint() + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); + } + } ++#endif + } + + QColor QGstreamerVideoWindow::colorKey() const +@@ -296,32 +343,22 @@ QSize QGstreamerVideoWindow::nativeSize() const + return m_nativeSize; + } + +-void QGstreamerVideoWindow::padBufferProbe(GstPad *pad, GstBuffer * /* buffer */, gpointer user_data) ++void QGstreamerVideoWindow::probeCaps(GstCaps *caps) + { +- QGstreamerVideoWindow *control = reinterpret_cast(user_data); +- QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); +- gst_pad_remove_buffer_probe(pad, control->m_bufferProbeId); ++ QSize resolution = QGstUtils::capsCorrectedResolution(caps); ++ QMetaObject::invokeMethod( ++ this, ++ "updateNativeVideoSize", ++ Qt::QueuedConnection, ++ Q_ARG(QSize, resolution)); + } + +-void QGstreamerVideoWindow::updateNativeVideoSize() ++void QGstreamerVideoWindow::updateNativeVideoSize(const QSize &size) + { +- const QSize oldSize = m_nativeSize; +- m_nativeSize = QSize(); +- +- if (m_videoSink) { +- //find video native size to update video widget size hint +- GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); +- GstCaps *caps = gst_pad_get_negotiated_caps(pad); +- gst_object_unref(GST_OBJECT(pad)); +- +- if (caps) { +- m_nativeSize = QGstUtils::capsCorrectedResolution(caps); +- gst_caps_unref(caps); +- } +- } +- +- if (m_nativeSize != oldSize) ++ if (m_nativeSize != size) { ++ m_nativeSize = size; + emit nativeSizeChanged(); ++ } + } + + GstElement *QGstreamerVideoWindow::videoSink() +diff --git a/src/gsttools/qgstutils.cpp b/src/gsttools/qgstutils.cpp +index 556fc03..65124c9 100644 +--- a/src/gsttools/qgstutils.cpp ++++ b/src/gsttools/qgstutils.cpp +@@ -40,7 +40,14 @@ + #include + #include + #include ++#include + #include ++#include ++ ++#include ++#include ++ ++template static int lengthOf(const T (&)[N]) { return N; } + + #ifdef USE_V4L + # include +@@ -82,15 +89,24 @@ static void addTagToMap(const GstTagList *list, + 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 GST_CHECK_VERSION(1,0,0) ++ if (G_VALUE_TYPE(&val) == G_TYPE_DATE) { ++ const GDate *date = (const GDate *)g_value_get_boxed(&val); ++#else + if (G_VALUE_TYPE(&val) == GST_TYPE_DATE) { + const GDate *date = gst_value_get_date(&val); ++#endif + if (g_date_valid(date)) { + int year = g_date_get_year(date); + int month = g_date_get_month(date); +@@ -169,6 +185,42 @@ QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) + return size; + } + ++ ++#if GST_CHECK_VERSION(1,0,0) ++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 } ++}; ++ ++} ++#endif ++ + /*! + Returns audio format for caps. + If caps doesn't have a valid audio format, an empty QAudioFormat is returned. +@@ -176,9 +228,26 @@ QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) + + QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps) + { +- const GstStructure *structure = gst_caps_get_structure(caps, 0); +- + QAudioFormat format; ++#if GST_CHECK_VERSION(1,0,0) ++ 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; ++ } ++ } ++#else ++ const GstStructure *structure = gst_caps_get_structure(caps, 0); + + if (qstrcmp(gst_structure_get_name(structure), "audio/x-raw-int") == 0) { + +@@ -249,16 +318,28 @@ QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps) + } else { + return QAudioFormat(); + } +- ++#endif + return format; + } + ++#if GST_CHECK_VERSION(1,0,0) ++/*! ++ 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); ++} ++#else + /*! + Returns audio format for a buffer. + If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. + */ +- + QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer) + { + GstCaps* caps = gst_buffer_get_caps(buffer); +@@ -269,7 +350,7 @@ QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer) + gst_caps_unref(caps); + return format; + } +- ++#endif + + /*! + Builds GstCaps for an audio format. +@@ -277,8 +358,32 @@ QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer) + Caller must unref GstCaps. + */ + +-GstCaps *QGstUtils::capsForAudioFormat(QAudioFormat format) ++GstCaps *QGstUtils::capsForAudioFormat(const QAudioFormat &format) + { ++ if (!format.isValid()) ++ return 0; ++ ++#if GST_CHECK_VERSION(1,0,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(), ++ NULL); ++ } ++ return 0; ++#else + GstStructure *structure = 0; + + if (format.isValid()) { +@@ -313,6 +418,7 @@ GstCaps *QGstUtils::capsForAudioFormat(QAudioFormat format) + } + + return caps; ++#endif + } + + void QGstUtils::initializeGst() +@@ -576,10 +682,629 @@ QByteArray QGstUtils::cameraDriver(const QString &device, GstElementFactory *fac + return QByteArray(); + } + ++QSet QGstUtils::supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)) ++{ ++ QSet supportedMimeTypes; ++ ++ //enumerate supported mime types ++ gst_init(NULL, NULL); ++ ++#if GST_CHECK_VERSION(1,0,0) ++ GstRegistry *registry = gst_registry_get(); ++ GList *orig_plugins = gst_registry_get_plugin_list(registry); ++#else ++ GstRegistry *registry = gst_registry_get_default(); ++ GList *orig_plugins = gst_default_registry_get_plugin_list (); ++#endif ++ for (GList *plugins = orig_plugins; plugins; plugins = g_list_next(plugins)) { ++ GstPlugin *plugin = (GstPlugin *) (plugins->data); ++#if GST_CHECK_VERSION(1,0,0) ++ if (GST_OBJECT_FLAG_IS_SET(GST_OBJECT(plugin), GST_PLUGIN_FLAG_BLACKLISTED)) ++ continue; ++#else ++ if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED ++ continue; ++#endif ++ ++ 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 == NULL)) ++ continue; ++ ++ GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); ++ GstElementFactory *factory; ++ ++ if (GST_IS_TYPE_FIND_FACTORY(feature)) { ++ QString name(gst_plugin_feature_get_name(feature)); ++ if (name.contains('/')) //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(gst_structure_get_name(structure)).toLower(); ++ ++ supportedMimeTypes.insert(nameLowcase); ++ if (nameLowcase.contains("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(str); ++ QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); ++ foreach (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) { ++ foreach (const QString &type, 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 } ++}; ++ ++} ++ ++#if GST_CHECK_VERSION(1,0,0) ++QImage QGstUtils::bufferToImage(GstBuffer *buffer, const GstVideoInfo &videoInfo) ++#else ++QImage QGstUtils::bufferToImage(GstBuffer *buffer) ++#endif ++{ ++ QImage img; ++ ++#if GST_CHECK_VERSION(1,0,0) ++ GstVideoInfo info = videoInfo; ++ GstVideoFrame frame; ++ if (!gst_video_frame_map(&frame, &info, buffer, GST_MAP_READ)) ++ return img; ++#else ++ GstCaps *caps = gst_buffer_get_caps(buffer); ++ if (!caps) ++ return img; ++ ++ GstStructure *structure = gst_caps_get_structure (caps, 0); ++ gint width = 0; ++ gint height = 0; ++ ++ if (!structure ++ || !gst_structure_get_int(structure, "width", &width) ++ || !gst_structure_get_int(structure, "height", &height) ++ || width <= 0 ++ || height <= 0) { ++ gst_caps_unref(caps); ++ return img; ++ } ++ gst_caps_unref(caps); ++#endif ++ ++#if GST_CHECK_VERSION(1,0,0) ++ 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]) ++ }; ++#else ++ if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { ++ const int stride[] = { width, width / 2, width / 2 }; ++ const uchar *data[] = { ++ (const uchar *)buffer->data, ++ (const uchar *)buffer->data + width * height, ++ (const uchar *)buffer->data + width * height * 5 / 4 ++ }; ++#endif ++ 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); ++#else ++ } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { ++ QImage::Format format = QImage::Format_Invalid; ++ int bpp = 0; ++ gst_structure_get_int(structure, "bpp", &bpp); ++ ++ if (bpp == 24) ++ format = QImage::Format_RGB888; ++ else if (bpp == 32) ++ format = QImage::Format_RGB32; ++ ++ if (format != QImage::Format_Invalid) { ++ img = QImage((const uchar *)buffer->data, ++ width, ++ height, ++ format); ++ img.bits(); //detach ++ } ++ } ++#endif ++ return img; ++} ++ ++ ++namespace { ++ ++#if GST_CHECK_VERSION(1,0,0) ++ ++struct VideoFormat ++{ ++ QVideoFrame::PixelFormat pixelFormat; ++ GstVideoFormat gstFormat; ++}; ++ ++static const VideoFormat qt_videoFormatLookup[] = ++{ ++ { QVideoFrame::Format_YUV420P, GST_VIDEO_FORMAT_I420 }, ++ { 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_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_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; ++} ++ ++#else ++ ++struct YuvFormat ++{ ++ QVideoFrame::PixelFormat pixelFormat; ++ guint32 fourcc; ++ int bitsPerPixel; ++}; ++ ++static const YuvFormat qt_yuvColorLookup[] = ++{ ++ { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, ++ { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, ++ { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, ++ { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, ++ { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, ++ { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 }, ++ { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 } ++}; ++ ++static int indexOfYuvColor(QVideoFrame::PixelFormat format) ++{ ++ const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); ++ ++ for (int i = 0; i < count; ++i) ++ if (qt_yuvColorLookup[i].pixelFormat == format) ++ return i; ++ ++ return -1; ++} ++ ++static int indexOfYuvColor(guint32 fourcc) ++{ ++ const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); ++ ++ for (int i = 0; i < count; ++i) ++ if (qt_yuvColorLookup[i].fourcc == fourcc) ++ return i; ++ ++ return -1; ++} ++ ++struct RgbFormat ++{ ++ QVideoFrame::PixelFormat pixelFormat; ++ int bitsPerPixel; ++ int depth; ++ int endianness; ++ int red; ++ int green; ++ int blue; ++ int alpha; ++}; ++ ++static const RgbFormat qt_rgbColorLookup[] = ++{ ++ { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x00000000 }, ++ { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, ++ { QVideoFrame::Format_BGR32 , 32, 24, 4321, int(0xFF000000), 0x00FF0000, 0x0000FF00, 0x00000000 }, ++ { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, ++ { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x000000FF }, ++ { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, int(0xFF000000) }, ++ { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, ++ { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, ++ { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } ++}; ++ ++static int indexOfRgbColor( ++ int bits, int depth, int endianness, int red, int green, int blue, int alpha) ++{ ++ const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); ++ ++ for (int i = 0; i < count; ++i) { ++ if (qt_rgbColorLookup[i].bitsPerPixel == bits ++ && qt_rgbColorLookup[i].depth == depth ++ && qt_rgbColorLookup[i].endianness == endianness ++ && qt_rgbColorLookup[i].red == red ++ && qt_rgbColorLookup[i].green == green ++ && qt_rgbColorLookup[i].blue == blue ++ && qt_rgbColorLookup[i].alpha == alpha) { ++ return i; ++ } ++ } ++ return -1; ++} ++#endif ++ ++} ++ ++#if GST_CHECK_VERSION(1,0,0) ++ ++QVideoSurfaceFormat QGstUtils::formatForCaps( ++ GstCaps *caps, GstVideoInfo *info, QAbstractVideoBuffer::HandleType handleType) ++{ ++ if (gst_video_info_from_caps(info, caps)) { ++ int index = indexOfVideoFormat(info->finfo->format); ++ ++ if (index != -1) { ++ QVideoSurfaceFormat format( ++ QSize(info->width, info->height), ++ qt_videoFormatLookup[index].pixelFormat, ++ handleType); ++ ++ if (info->fps_d > 0) ++ format.setFrameRate(qreal(info->fps_d) / info->fps_n); ++ ++ if (info->par_d > 0) ++ format.setPixelAspectRatio(info->par_n, info->par_d); ++ ++ return format; ++ } ++ } ++ return QVideoSurfaceFormat(); ++} ++ ++#else ++ ++QVideoSurfaceFormat QGstUtils::formatForCaps( ++ GstCaps *caps, int *bytesPerLine, QAbstractVideoBuffer::HandleType handleType) ++{ ++ const GstStructure *structure = gst_caps_get_structure(caps, 0); ++ ++ QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; ++ int bitsPerPixel = 0; ++ ++ QSize size; ++ gst_structure_get_int(structure, "width", &size.rwidth()); ++ gst_structure_get_int(structure, "height", &size.rheight()); ++ ++ if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { ++ guint32 fourcc = 0; ++ gst_structure_get_fourcc(structure, "format", &fourcc); ++ ++ int index = indexOfYuvColor(fourcc); ++ if (index != -1) { ++ pixelFormat = qt_yuvColorLookup[index].pixelFormat; ++ bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel; ++ } ++ } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { ++ int depth = 0; ++ int endianness = 0; ++ int red = 0; ++ int green = 0; ++ int blue = 0; ++ int alpha = 0; ++ ++ gst_structure_get_int(structure, "bpp", &bitsPerPixel); ++ gst_structure_get_int(structure, "depth", &depth); ++ gst_structure_get_int(structure, "endianness", &endianness); ++ gst_structure_get_int(structure, "red_mask", &red); ++ gst_structure_get_int(structure, "green_mask", &green); ++ gst_structure_get_int(structure, "blue_mask", &blue); ++ gst_structure_get_int(structure, "alpha_mask", &alpha); ++ ++ int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); ++ ++ if (index != -1) ++ pixelFormat = qt_rgbColorLookup[index].pixelFormat; ++ } ++ ++ if (pixelFormat != QVideoFrame::Format_Invalid) { ++ QVideoSurfaceFormat format(size, pixelFormat, handleType); ++ ++ QPair rate; ++ gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second); ++ ++ if (rate.second) ++ format.setFrameRate(qreal(rate.first)/rate.second); ++ ++ gint aspectNum = 0; ++ gint aspectDenum = 0; ++ if (gst_structure_get_fraction( ++ structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { ++ if (aspectDenum > 0) ++ format.setPixelAspectRatio(aspectNum, aspectDenum); ++ } ++ ++ if (bytesPerLine) ++ *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; ++ ++ return format; ++ } ++ return QVideoSurfaceFormat(); ++} ++ ++#endif ++ ++GstCaps *QGstUtils::capsForFormats(const QList &formats) ++{ ++ GstCaps *caps = gst_caps_new_empty(); ++ ++#if GST_CHECK_VERSION(1,0,0) ++ foreach (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), ++ NULL)); ++ } ++ } ++#else ++ foreach (QVideoFrame::PixelFormat format, formats) { ++ int index = indexOfYuvColor(format); ++ ++ if (index != -1) { ++ gst_caps_append_structure(caps, gst_structure_new( ++ "video/x-raw-yuv", ++ "format", GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, ++ NULL)); ++ continue; ++ } ++ ++ const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); ++ ++ for (int i = 0; i < count; ++i) { ++ if (qt_rgbColorLookup[i].pixelFormat == format) { ++ GstStructure *structure = gst_structure_new( ++ "video/x-raw-rgb", ++ "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, ++ "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, ++ "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, ++ "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, ++ "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, ++ "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, ++ NULL); ++ ++ if (qt_rgbColorLookup[i].alpha != 0) { ++ gst_structure_set( ++ structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL); ++ } ++ gst_caps_append_structure(caps, structure); ++ } ++ } ++ } ++#endif ++ ++ 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, ++ NULL); ++ ++ 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)); ++ ++ QMapIterator it(data); ++ while (it.hasNext()) { ++ it.next(); ++ const QString tagName = it.key(); ++ const QVariant tagValue = it.value(); ++ ++ switch (tagValue.type()) { ++ case QVariant::String: ++ gst_tag_setter_add_tags(GST_TAG_SETTER(element), ++ GST_TAG_MERGE_REPLACE, ++ tagName.toUtf8().constData(), ++ tagValue.toString().toUtf8().constData(), ++ NULL); ++ break; ++ case QVariant::Int: ++ case QVariant::LongLong: ++ gst_tag_setter_add_tags(GST_TAG_SETTER(element), ++ GST_TAG_MERGE_REPLACE, ++ tagName.toUtf8().constData(), ++ tagValue.toInt(), ++ NULL); ++ break; ++ case QVariant::Double: ++ gst_tag_setter_add_tags(GST_TAG_SETTER(element), ++ GST_TAG_MERGE_REPLACE, ++ tagName.toUtf8().constData(), ++ tagValue.toDouble(), ++ NULL); ++ break; ++ case QVariant::DateTime: { ++ 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()), ++ NULL); ++ break; ++ } ++ default: ++ break; ++ } ++ } ++} ++ ++void QGstUtils::setMetaData(GstBin *bin, const QMap &data) ++{ ++ GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER); ++#if GST_CHECK_VERSION(1,0,0) ++ GValue item = G_VALUE_INIT; ++ while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) { ++ GstElement * const element = GST_ELEMENT(g_value_get_object(&item)); ++#else ++ GstElement *element = 0; ++ while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { ++#endif ++ setMetaData(element, data); ++ } ++ gst_iterator_free(elements); ++} ++ ++ ++GstCaps *QGstUtils::videoFilterCaps() ++{ ++ static GstStaticCaps staticCaps = GST_STATIC_CAPS( ++#if GST_CHECK_VERSION(1,2,0) ++ "video/x-raw(ANY);" ++#elif GST_CHECK_VERSION(1,0,0) ++ "video/x-raw;" ++#else ++ "video/x-raw-yuv;" ++ "video/x-raw-rgb;" ++ "video/x-raw-data;" ++ "video/x-android-buffer;" ++#endif ++ "image/jpeg;" ++ "video/x-h264"); ++ ++ return gst_caps_make_writable(gst_static_caps_get(&staticCaps)); ++} + + void qt_gst_object_ref_sink(gpointer object) + { +-#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 24) ++#if GST_CHECK_VERSION(0,10,24) + gst_object_ref_sink(object); + #else + g_return_if_fail (GST_IS_OBJECT(object)); +@@ -595,4 +1320,50 @@ void qt_gst_object_ref_sink(gpointer object) + #endif + } + ++GstCaps *qt_gst_pad_get_current_caps(GstPad *pad) ++{ ++#if GST_CHECK_VERSION(1,0,0) ++ return gst_pad_get_current_caps(pad); ++#else ++ return gst_pad_get_negotiated_caps(pad); ++#endif ++} ++ ++GstStructure *qt_gst_structure_new_empty(const char *name) ++{ ++#if GST_CHECK_VERSION(1,0,0) ++ return gst_structure_new_empty(name); ++#else ++ return gst_structure_new(name, NULL); ++#endif ++} ++ ++gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur) ++{ ++#if GST_CHECK_VERSION(1,0,0) ++ return gst_element_query_position(element, format, cur); ++#else ++ return gst_element_query_position(element, &format, cur); ++#endif ++} ++ ++gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur) ++{ ++#if GST_CHECK_VERSION(1,0,0) ++ return gst_element_query_duration(element, format, cur); ++#else ++ return gst_element_query_duration(element, &format, cur); ++#endif ++} ++ ++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/qgstvideobuffer.cpp b/src/gsttools/qgstvideobuffer.cpp +index 18702ec..1ce07ca 100644 +--- a/src/gsttools/qgstvideobuffer.cpp ++++ b/src/gsttools/qgstvideobuffer.cpp +@@ -35,21 +35,35 @@ + + QT_BEGIN_NAMESPACE + ++#if GST_CHECK_VERSION(1,0,0) ++QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info) ++ : QAbstractPlanarVideoBuffer(NoHandle) ++ , m_videoInfo(info) ++#else + QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine) + : QAbstractVideoBuffer(NoHandle) +- , m_buffer(buffer) + , m_bytesPerLine(bytesPerLine) ++#endif ++ , m_buffer(buffer) + , m_mode(NotMapped) + { + gst_buffer_ref(m_buffer); + } + ++#if GST_CHECK_VERSION(1,0,0) ++QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, ++ QGstVideoBuffer::HandleType handleType, ++ const QVariant &handle) ++ : QAbstractPlanarVideoBuffer(handleType) ++ , m_videoInfo(info) ++#else + QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, + QGstVideoBuffer::HandleType handleType, + const QVariant &handle) + : QAbstractVideoBuffer(handleType) +- , m_buffer(buffer) + , m_bytesPerLine(bytesPerLine) ++#endif ++ , m_buffer(buffer) + , m_mode(NotMapped) + , m_handle(handle) + { +@@ -58,6 +72,8 @@ QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, + + QGstVideoBuffer::~QGstVideoBuffer() + { ++ unmap(); ++ + gst_buffer_unref(m_buffer); + } + +@@ -67,12 +83,49 @@ QAbstractVideoBuffer::MapMode QGstVideoBuffer::mapMode() const + return m_mode; + } + ++#if GST_CHECK_VERSION(1,0,0) ++ ++int QGstVideoBuffer::map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) ++{ ++ const GstMapFlags flags = GstMapFlags(((mode & ReadOnly) ? GST_MAP_READ : 0) ++ | ((mode & WriteOnly) ? GST_MAP_WRITE : 0)); ++ ++ if (mode == NotMapped || m_mode != NotMapped) { ++ return 0; ++ } else if (m_videoInfo.finfo->n_planes == 0) { // Encoded ++ if (gst_buffer_map(m_buffer, &m_frame.map[0], flags)) { ++ if (numBytes) ++ *numBytes = m_frame.map[0].size; ++ bytesPerLine[0] = -1; ++ data[0] = static_cast(m_frame.map[0].data); ++ ++ m_mode = mode; ++ ++ return 1; ++ } ++ } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, flags)) { ++ if (numBytes) ++ *numBytes = m_frame.info.size; ++ ++ for (guint i = 0; i < m_frame.info.finfo->n_planes; ++i) { ++ bytesPerLine[i] = m_frame.info.stride[i]; ++ data[i] = static_cast(m_frame.data[i]); ++ } ++ ++ m_mode = mode; ++ ++ return m_frame.info.finfo->n_planes; ++ } ++ return 0; ++} ++ ++#else ++ + uchar *QGstVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) + { + if (mode != NotMapped && m_mode == NotMapped) { + if (numBytes) + *numBytes = m_buffer->size; +- + if (bytesPerLine) + *bytesPerLine = m_bytesPerLine; + +@@ -83,8 +136,19 @@ uchar *QGstVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) + return 0; + } + } ++ ++#endif ++ + void QGstVideoBuffer::unmap() + { ++#if GST_CHECK_VERSION(1,0,0) ++ 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); ++ } ++#endif + m_mode = NotMapped; + } + +diff --git a/src/gsttools/qgstvideorendererplugin.cpp b/src/gsttools/qgstvideorendererplugin.cpp +new file mode 100644 +index 0000000..5eda85a +--- /dev/null ++++ b/src/gsttools/qgstvideorendererplugin.cpp +@@ -0,0 +1,53 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Jolla Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 2.1 requirements ++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3.0 as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 3.0 requirements will be ++** met: http://www.gnu.org/copyleft/gpl.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/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp +new file mode 100644 +index 0000000..1102c2a +--- /dev/null ++++ b/src/gsttools/qgstvideorenderersink.cpp +@@ -0,0 +1,605 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Jolla Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 2.1 requirements ++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3.0 as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 3.0 requirements will be ++** met: http://www.gnu.org/copyleft/gpl.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" ++ ++//#define DEBUG_VIDEO_SURFACE_SINK ++ ++QT_BEGIN_NAMESPACE ++ ++QGstDefaultVideoRenderer::QGstDefaultVideoRenderer() ++ : m_flushed(true) ++{ ++} ++ ++QGstDefaultVideoRenderer::~QGstDefaultVideoRenderer() ++{ ++} ++ ++GstCaps *QGstDefaultVideoRenderer::getCaps(QAbstractVideoSurface *surface) ++{ ++ return QGstUtils::capsForFormats(surface->supportedPixelFormats()); ++} ++ ++bool QGstDefaultVideoRenderer::start(QAbstractVideoSurface *surface, GstCaps *caps) ++{ ++ m_flushed = true; ++ m_format = QGstUtils::formatForCaps(caps, &m_videoInfo); ++ ++ 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; ++ QVideoFrame frame( ++ new QGstVideoBuffer(buffer, m_videoInfo), ++ 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(QMediaPluginLoader, rendererLoader, ++ (QGstVideoRendererInterface_iid, QLatin1String("video/gstvideorenderer"), Qt::CaseInsensitive)) ++ ++QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface) ++ : m_surface(surface) ++ , m_renderer(0) ++ , m_activeRenderer(0) ++ , m_surfaceCaps(0) ++ , m_startCaps(0) ++ , m_lastBuffer(0) ++ , m_notified(false) ++ , m_stop(false) ++ , m_render(false) ++ , m_flush(false) ++{ ++ foreach (QObject *instance, rendererLoader()->instances(QGstVideoRendererPluginKey)) { ++ QGstVideoRendererInterface* plugin = qobject_cast(instance); ++ if (QGstVideoRenderer *renderer = plugin ? plugin->createRenderer() : 0) ++ m_renderers.append(renderer); ++ } ++ ++ 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); ++} ++ ++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; ++ } ++ ++ m_render = false; ++ ++ if (m_lastBuffer) { ++ gst_buffer_unref(m_lastBuffer); ++ m_lastBuffer = 0; ++ } ++ ++ 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; ++ } ++ ++ if (m_lastBuffer) { ++ gst_buffer_unref(m_lastBuffer); ++ m_lastBuffer = 0; ++ } ++ ++ waitForAsyncEvent(&locker, &m_setupCondition, 500); ++} ++ ++bool QVideoSurfaceGstDelegate::proposeAllocation(GstQuery *query) ++{ ++ QMutexLocker locker(&m_mutex); ++ ++ if (QGstVideoRenderer *pool = m_activeRenderer) { ++ locker.unlock(); ++ ++ return pool->proposeAllocation(query); ++ } else { ++ return false; ++ } ++} ++ ++void QVideoSurfaceGstDelegate::flush() ++{ ++ QMutexLocker locker(&m_mutex); ++ ++ m_flush = true; ++ m_render = false; ++ ++ if (m_lastBuffer) { ++ gst_buffer_unref(m_lastBuffer); ++ m_lastBuffer = 0; ++ } ++ ++ notify(); ++} ++ ++GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer, bool show) ++{ ++ QMutexLocker locker(&m_mutex); ++ ++ if (m_lastBuffer) ++ gst_buffer_unref(m_lastBuffer); ++ m_lastBuffer = buffer; ++ gst_buffer_ref(m_lastBuffer); ++ ++ if (show) { ++ m_render = true; ++ ++ return waitForAsyncEvent(&locker, &m_renderCondition, 300) ++ ? m_renderReturn ++ : GST_FLOW_ERROR; ++ } else { ++ return GST_FLOW_OK; ++ } ++} ++ ++void QVideoSurfaceGstDelegate::handleShowPrerollChange(GObject *object, GParamSpec *, gpointer d) ++{ ++ QVideoSurfaceGstDelegate * const delegate = static_cast(d); ++ ++ gboolean showPreroll = true; // "show-preroll-frame" property is true by default ++ g_object_get(object, "show-preroll-frame", &showPreroll, NULL); ++ ++ GstState state = GST_STATE_NULL; ++ GstState pendingState = GST_STATE_NULL; ++ gst_element_get_state(GST_ELEMENT(object), &state, &pendingState, 0); ++ ++ const bool paused ++ = (pendingState == GST_STATE_VOID_PENDING && state == GST_STATE_PAUSED) ++ || pendingState == GST_STATE_PAUSED; ++ ++ if (paused) { ++ QMutexLocker locker(&delegate->m_mutex); ++ ++ if (!showPreroll && delegate->m_lastBuffer) { ++ delegate->m_render = false; ++ delegate->m_flush = true; ++ delegate->notify(); ++ } else if (delegate->m_lastBuffer) { ++ delegate->m_render = true; ++ delegate->notify(); ++ } ++ } ++} ++ ++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; ++ } else { ++ 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_render) { ++ m_render = false; ++ ++ if (m_activeRenderer && m_surface && m_lastBuffer) { ++ GstBuffer *buffer = m_lastBuffer; ++ gst_buffer_ref(buffer); ++ ++ locker->unlock(); ++ ++ const bool rendered = m_activeRenderer->present(m_surface, buffer); ++ ++ gst_buffer_unref(buffer); ++ ++ locker->relock(); ++ ++ m_renderReturn = rendered ++ ? GST_FLOW_OK ++ : GST_FLOW_ERROR; ++ ++ m_renderCondition.wakeAll(); ++ } else { ++ m_renderReturn = GST_FLOW_ERROR; ++ 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; ++ } else { ++ notify(); ++ ++ return condition->wait(&m_mutex, time); ++ } ++} ++ ++void QVideoSurfaceGstDelegate::updateSupportedFormats() ++{ ++ if (m_surfaceCaps) { ++ gst_caps_unref(m_surfaceCaps); ++ m_surfaceCaps = 0; ++ } ++ ++ foreach (QGstVideoRenderer *pool, 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; ++ } else { ++ gst_caps_unref(caps); ++ } ++ } ++} ++ ++static GstVideoSinkClass *sink_parent_class; ++ ++#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast(s)) ++ ++QGstVideoRendererSink *QGstVideoRendererSink::createSink(QAbstractVideoSurface *surface) ++{ ++ QGstVideoRendererSink *sink = reinterpret_cast( ++ g_object_new(QGstVideoRendererSink::get_type(), 0)); ++ ++ sink->delegate = new QVideoSurfaceGstDelegate(surface); ++ ++ g_signal_connect( ++ G_OBJECT(sink), ++ "notify::show-preroll-frame", ++ G_CALLBACK(QVideoSurfaceGstDelegate::handleShowPrerollChange), ++ sink->delegate); ++ ++ return sink; ++} ++ ++GType QGstVideoRendererSink::get_type() ++{ ++ static GType type = 0; ++ ++ if (type == 0) { ++ static const GTypeInfo info = ++ { ++ sizeof(QGstVideoRendererSinkClass), // class_size ++ base_init, // base_init ++ NULL, // base_finalize ++ class_init, // class_init ++ NULL, // class_finalize ++ NULL, // 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)); ++ } ++ ++ 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)); ++ ++ 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->preroll = QGstVideoRendererSink::preroll; ++ base_sink_class->render = QGstVideoRendererSink::render; ++ ++ GstElementClass *element_class = reinterpret_cast(g_class); ++ element_class->change_state = QGstVideoRendererSink::change_state; ++ ++ 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)); ++} ++ ++void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class) ++{ ++ VO_SINK(instance); ++ ++ Q_UNUSED(g_class); ++ ++ sink->delegate = 0; ++} ++ ++void QGstVideoRendererSink::finalize(GObject *object) ++{ ++ VO_SINK(object); ++ ++ delete sink->delegate; ++ ++ // Chain up ++ G_OBJECT_CLASS(sink_parent_class)->finalize(object); ++} ++ ++GstStateChangeReturn QGstVideoRendererSink::change_state( ++ GstElement *element, GstStateChange transition) ++{ ++ Q_UNUSED(element); ++ ++ 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); ++} ++ ++GstFlowReturn QGstVideoRendererSink::preroll(GstBaseSink *base, GstBuffer *buffer) ++{ ++ VO_SINK(base); ++ ++ gboolean showPreroll = true; // "show-preroll-frame" property is true by default ++ g_object_get(G_OBJECT(base), "show-preroll-frame", &showPreroll, NULL); ++ ++ return sink->delegate->render(buffer, showPreroll); // display frame ++} ++ ++GstFlowReturn QGstVideoRendererSink::render(GstBaseSink *base, GstBuffer *buffer) ++{ ++ VO_SINK(base); ++ return sink->delegate->render(buffer, true); ++} ++ ++QT_END_NAMESPACE +diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp +index f3e2d88..147db66 100644 +--- a/src/gsttools/qvideosurfacegstsink.cpp ++++ b/src/gsttools/qvideosurfacegstsink.cpp +@@ -41,8 +41,13 @@ + #include + #include "qgstvideobuffer_p.h" + ++#include "qgstutils_p.h" + #include "qvideosurfacegstsink_p.h" + ++#if GST_VERSION_MAJOR >=1 ++#include ++#endif ++ + //#define DEBUG_VIDEO_SURFACE_SINK + + QT_BEGIN_NAMESPACE +@@ -62,10 +67,12 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate( + if (m_surface) { + foreach (QObject *instance, bufferPoolLoader()->instances(QGstBufferPoolPluginKey)) { + QGstBufferPoolInterface* plugin = qobject_cast(instance); ++ + if (plugin) { + m_pools.append(plugin); + } + } ++ + updateSupportedFormats(); + connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats())); + } +@@ -191,13 +198,15 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) + m_format.frameSize(), + m_format.pixelFormat()); + +- QVideoSurfaceGstSink::setFrameTimeStamps(&m_frame, buffer); ++ QGstUtils::setFrameTimeStamps(&m_frame, buffer); + + m_renderReturn = GST_FLOW_OK; + + if (QThread::currentThread() == thread()) { + if (!m_surface.isNull()) + m_surface->present(m_frame); ++ else ++ qWarning() << "m_surface.isNull()."; + } else { + QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection); + m_renderCondition.wait(&m_mutex, 300); +@@ -283,90 +292,6 @@ void QVideoSurfaceGstDelegate::updateSupportedFormats() + } + } + +-struct YuvFormat +-{ +- QVideoFrame::PixelFormat pixelFormat; +- guint32 fourcc; +- int bitsPerPixel; +-}; +- +-static const YuvFormat qt_yuvColorLookup[] = +-{ +- { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, +- { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, +- { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, +- { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, +- { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, +- { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 }, +- { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 } +-}; +- +-static int indexOfYuvColor(QVideoFrame::PixelFormat format) +-{ +- const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); +- +- for (int i = 0; i < count; ++i) +- if (qt_yuvColorLookup[i].pixelFormat == format) +- return i; +- +- return -1; +-} +- +-static int indexOfYuvColor(guint32 fourcc) +-{ +- const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); +- +- for (int i = 0; i < count; ++i) +- if (qt_yuvColorLookup[i].fourcc == fourcc) +- return i; +- +- return -1; +-} +- +-struct RgbFormat +-{ +- QVideoFrame::PixelFormat pixelFormat; +- int bitsPerPixel; +- int depth; +- int endianness; +- int red; +- int green; +- int blue; +- int alpha; +-}; +- +-static const RgbFormat qt_rgbColorLookup[] = +-{ +- { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x00000000 }, +- { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, +- { QVideoFrame::Format_BGR32 , 32, 24, 4321, int(0xFF000000), 0x00FF0000, 0x0000FF00, 0x00000000 }, +- { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, +- { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x000000FF }, +- { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, int(0xFF000000) }, +- { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, +- { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, +- { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } +-}; +- +-static int indexOfRgbColor( +- int bits, int depth, int endianness, int red, int green, int blue, int alpha) +-{ +- const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); +- +- for (int i = 0; i < count; ++i) { +- if (qt_rgbColorLookup[i].bitsPerPixel == bits +- && qt_rgbColorLookup[i].depth == depth +- && qt_rgbColorLookup[i].endianness == endianness +- && qt_rgbColorLookup[i].red == red +- && qt_rgbColorLookup[i].green == green +- && qt_rgbColorLookup[i].blue == blue +- && qt_rgbColorLookup[i].alpha == alpha) { +- return i; +- } +- } +- return -1; +-} +- + static GstVideoSinkClass *sink_parent_class; + + #define VO_SINK(s) QVideoSurfaceGstSink *sink(reinterpret_cast(s)) +@@ -494,8 +419,6 @@ GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) + { + VO_SINK(base); + +- GstCaps *caps = gst_caps_new_empty(); +- + // Find the supported pixel formats + // with buffer pool specific formats listed first + QList supportedFormats; +@@ -503,6 +426,7 @@ GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) + QList poolHandleFormats; + sink->delegate->poolMutex()->lock(); + QGstBufferPoolInterface *pool = sink->delegate->pool(); ++ + if (pool) + poolHandleFormats = sink->delegate->supportedPixelFormats(pool->handleType()); + sink->delegate->poolMutex()->unlock(); +@@ -513,47 +437,7 @@ GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) + supportedFormats.append(format); + } + +- foreach (QVideoFrame::PixelFormat format, supportedFormats) { +- int index = indexOfYuvColor(format); +- +- if (index != -1) { +- gst_caps_append_structure(caps, gst_structure_new( +- "video/x-raw-yuv", +- "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, +- "format" , GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, +- NULL)); +- continue; +- } +- +- const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); +- +- for (int i = 0; i < count; ++i) { +- if (qt_rgbColorLookup[i].pixelFormat == format) { +- GstStructure *structure = gst_structure_new( +- "video/x-raw-rgb", +- "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, +- "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, +- "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, +- "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, +- "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, +- "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, +- "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, +- NULL); +- +- if (qt_rgbColorLookup[i].alpha != 0) { +- gst_structure_set( +- structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL); +- } +- gst_caps_append_structure(caps, structure); +- } +- } +- } +- +- return caps; ++ return QGstUtils::capsForFormats(supportedFormats); + } + + gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) +@@ -575,7 +459,7 @@ gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) + QAbstractVideoBuffer::HandleType handleType = + pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle; + +- QVideoSurfaceFormat format = formatForCaps(caps, &bytesPerLine, handleType); ++ QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine, handleType); + + if (sink->delegate->isActive()) { + QVideoSurfaceFormat surfaceFormst = sink->delegate->surfaceFormat(); +@@ -592,7 +476,7 @@ gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) + sink->lastRequestedCaps = 0; + + #ifdef DEBUG_VIDEO_SURFACE_SINK +- qDebug() << "Staring video surface, format:"; ++ qDebug() << "Starting video surface, format:"; + qDebug() << format; + qDebug() << "bytesPerLine:" << bytesPerLine; + #endif +@@ -606,87 +490,6 @@ gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) + return FALSE; + } + +-QVideoSurfaceFormat QVideoSurfaceGstSink::formatForCaps(GstCaps *caps, int *bytesPerLine, QAbstractVideoBuffer::HandleType handleType) +-{ +- const GstStructure *structure = gst_caps_get_structure(caps, 0); +- +- QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; +- int bitsPerPixel = 0; +- +- QSize size; +- gst_structure_get_int(structure, "width", &size.rwidth()); +- gst_structure_get_int(structure, "height", &size.rheight()); +- +- if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { +- guint32 fourcc = 0; +- gst_structure_get_fourcc(structure, "format", &fourcc); +- +- int index = indexOfYuvColor(fourcc); +- if (index != -1) { +- pixelFormat = qt_yuvColorLookup[index].pixelFormat; +- bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel; +- } +- } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { +- int depth = 0; +- int endianness = 0; +- int red = 0; +- int green = 0; +- int blue = 0; +- int alpha = 0; +- +- gst_structure_get_int(structure, "bpp", &bitsPerPixel); +- gst_structure_get_int(structure, "depth", &depth); +- gst_structure_get_int(structure, "endianness", &endianness); +- gst_structure_get_int(structure, "red_mask", &red); +- gst_structure_get_int(structure, "green_mask", &green); +- gst_structure_get_int(structure, "blue_mask", &blue); +- gst_structure_get_int(structure, "alpha_mask", &alpha); +- +- int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); +- +- if (index != -1) +- pixelFormat = qt_rgbColorLookup[index].pixelFormat; +- } +- +- if (pixelFormat != QVideoFrame::Format_Invalid) { +- QVideoSurfaceFormat format(size, pixelFormat, handleType); +- +- QPair rate; +- gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second); +- +- if (rate.second) +- format.setFrameRate(qreal(rate.first)/rate.second); +- +- gint aspectNum = 0; +- gint aspectDenum = 0; +- if (gst_structure_get_fraction( +- structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { +- if (aspectDenum > 0) +- format.setPixelAspectRatio(aspectNum, aspectDenum); +- } +- +- if (bytesPerLine) +- *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; +- +- return format; +- } +- +- return QVideoSurfaceFormat(); +-} +- +-void QVideoSurfaceGstSink::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)); +- } +-} +- + GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer) + { +@@ -731,7 +534,7 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + + if (sink->delegate->isActive()) { + //if format was changed, restart the surface +- QVideoSurfaceFormat format = formatForCaps(intersection); ++ QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection); + QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); + + if (format.pixelFormat() != surfaceFormat.pixelFormat() || +@@ -749,7 +552,7 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + QAbstractVideoBuffer::HandleType handleType = + pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle; + +- QVideoSurfaceFormat format = formatForCaps(intersection, &bytesPerLine, handleType); ++ QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection, &bytesPerLine, handleType); + + if (!sink->delegate->start(format, bytesPerLine)) { + qWarning() << "failed to start video surface"; +@@ -763,7 +566,7 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); + + if (!pool->isFormatSupported(surfaceFormat)) { +- //qDebug() << "sink doesn't support native pool format, skip custom buffers allocation"; ++ qDebug() << "sink doesn't support native pool format, skip custom buffers allocation"; + return GST_FLOW_OK; + } + +@@ -787,7 +590,6 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + gboolean QVideoSurfaceGstSink::start(GstBaseSink *base) + { + Q_UNUSED(base); +- + return TRUE; + } + +diff --git a/src/multimedia/gsttools_headers/qgstappsrc_p.h b/src/multimedia/gsttools_headers/qgstappsrc_p.h +index 4af9252..0e0fc0a 100644 +--- a/src/multimedia/gsttools_headers/qgstappsrc_p.h ++++ b/src/multimedia/gsttools_headers/qgstappsrc_p.h +@@ -39,7 +39,10 @@ + + #include + #include ++ ++#if GST_VERSION_MAJOR < 1 + #include ++#endif + + QT_BEGIN_NAMESPACE + +diff --git a/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h b/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h +index 34669b8..571a7ce 100644 +--- a/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h ++++ b/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h +@@ -38,23 +38,32 @@ + #include + #include + #include ++#include ++ ++#include + + QT_BEGIN_NAMESPACE + +-class QGstreamerAudioProbeControl : public QMediaAudioProbeControl ++class QGstreamerAudioProbeControl ++ : public QMediaAudioProbeControl ++ , public QGstreamerBufferProbe ++ , public QSharedData + { + Q_OBJECT + public: + explicit QGstreamerAudioProbeControl(QObject *parent); + virtual ~QGstreamerAudioProbeControl(); + +- void bufferProbed(GstBuffer* buffer); ++protected: ++ void probeCaps(GstCaps *caps); ++ bool probeBuffer(GstBuffer *buffer); + + private slots: + void bufferProbed(); + + private: + QAudioBuffer m_pendingBuffer; ++ QAudioFormat m_format; + QMutex m_bufferMutex; + }; + +diff --git a/src/multimedia/gsttools_headers/qgstreamerbufferprobe_p.h b/src/multimedia/gsttools_headers/qgstreamerbufferprobe_p.h +new file mode 100644 +index 0000000..9240742 +--- /dev/null ++++ b/src/multimedia/gsttools_headers/qgstreamerbufferprobe_p.h +@@ -0,0 +1,86 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Jolla Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 2.1 requirements ++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3.0 as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 3.0 requirements will be ++** met: http://www.gnu.org/copyleft/gpl.html. ++** ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef QGSTREAMERBUFFERPROBE_H ++#define QGSTREAMERBUFFERPROBE_H ++ ++#include ++ ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++class 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: ++#if GST_CHECK_VERSION(1,0,0) ++ static GstPadProbeReturn capsProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); ++ static GstPadProbeReturn bufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); ++ int m_capsProbeId; ++#else ++ static gboolean bufferProbe(GstElement *element, GstBuffer *buffer, gpointer user_data); ++ GstCaps *m_caps; ++#endif ++ int m_bufferProbeId; ++ const Flags m_flags; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif // QGSTREAMERAUDIOPROBECONTROL_H +diff --git a/src/multimedia/gsttools_headers/qgstreamermirtexturerenderer_p.h b/src/multimedia/gsttools_headers/qgstreamermirtexturerenderer_p.h +new file mode 100644 +index 0000000..25b8d60 +--- /dev/null ++++ b/src/multimedia/gsttools_headers/qgstreamermirtexturerenderer_p.h +@@ -0,0 +1,102 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Canonical Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** This file is part of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:LGPL21$ ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free ++** Software Foundation and appearing in the file LICENSE.LGPLv21 and ++** LICENSE.LGPLv3 included in the packaging of this file. Please review the ++** following information to ensure the GNU Lesser General Public License ++** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ++** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef QGSTREAMERMIRTEXTURERENDERER_H ++#define QGSTREAMERMIRTEXTURERENDERER_H ++ ++#include ++#include ++#include ++#include ++ ++#include "qgstreamervideorendererinterface_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++class QGstreamerMirTextureBuffer; ++class QGstreamerPlayerSession; ++class QGLContext; ++class QOpenGLContext; ++class QSurfaceFormat; ++ ++class QGstreamerMirTextureRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface ++{ ++ Q_OBJECT ++ Q_INTERFACES(QGstreamerVideoRendererInterface) ++public: ++ QGstreamerMirTextureRenderer(QObject *parent = 0, const QGstreamerPlayerSession *playerSession = 0); ++ virtual ~QGstreamerMirTextureRenderer(); ++ ++ QAbstractVideoSurface *surface() const; ++ void setSurface(QAbstractVideoSurface *surface); ++ ++ void setPlayerSession(const QGstreamerPlayerSession *playerSession); ++ ++ GstElement *videoSink(); ++ ++ void stopRenderer(); ++ bool isReady() const { return m_surface != 0; } ++ ++signals: ++ void sinkChanged(); ++ void readyChanged(bool); ++ void nativeSizeChanged(); ++ ++private slots: ++ void handleFormatChange(); ++ void updateNativeVideoSize(); ++ void handleFocusWindowChanged(QWindow *window); ++ void renderFrame(); ++ ++private: ++ QWindow *createOffscreenWindow(const QSurfaceFormat &format); ++ static void handleFrameReady(gpointer userData); ++ static GstPadProbeReturn padBufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer userData); ++ ++ GstElement *m_videoSink; ++ QPointer m_surface; ++ QPointer m_glSurface; ++ QGLContext *m_context; ++ QOpenGLContext *m_glContext; ++ unsigned int m_textureId; ++ QWindow *m_offscreenSurface; ++ QGstreamerPlayerSession *m_playerSession; ++ QGstreamerMirTextureBuffer *m_textureBuffer; ++ QSize m_nativeSize; ++ ++ QMutex m_mutex; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif // QGSTREAMERMIRTEXTURERENDRER_H +diff --git a/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h b/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h +index 49064f9..f035f65 100644 +--- a/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h ++++ b/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h +@@ -35,20 +35,29 @@ + #define QGSTREAMERVIDEOPROBECONTROL_H + + #include ++#include + #include + #include + #include ++#include ++ ++#include + + QT_BEGIN_NAMESPACE + +-class QGstreamerVideoProbeControl : public QMediaVideoProbeControl ++class QGstreamerVideoProbeControl ++ : public QMediaVideoProbeControl ++ , public QGstreamerBufferProbe ++ , public QSharedData + { + Q_OBJECT + public: + explicit QGstreamerVideoProbeControl(QObject *parent); + virtual ~QGstreamerVideoProbeControl(); + +- void bufferProbed(GstBuffer* buffer); ++ void probeCaps(GstCaps *caps); ++ bool probeBuffer(GstBuffer *buffer); ++ + void startFlushing(); + void stopFlushing(); + +@@ -56,10 +65,16 @@ private slots: + void frameProbed(); + + private: +- bool m_flushing; +- bool m_frameProbed; // true if at least one frame was probed ++ QVideoSurfaceFormat m_format; + QVideoFrame m_pendingFrame; + QMutex m_frameMutex; ++#if GST_CHECK_VERSION(1,0,0) ++ GstVideoInfo m_videoInfo; ++#else ++ int m_bytesPerLine; ++#endif ++ bool m_flushing; ++ bool m_frameProbed; // true if at least one frame was probed + }; + + QT_END_NAMESPACE +diff --git a/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h b/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h +index 81e5764..d38156c 100644 +--- a/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h ++++ b/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h +@@ -38,6 +38,7 @@ + + #include "qgstreamervideorendererinterface_p.h" + #include ++#include + #include + + QT_BEGIN_NAMESPACE +@@ -45,7 +46,8 @@ class QAbstractVideoSurface; + + class QGstreamerVideoWindow : public QVideoWindowControl, + public QGstreamerVideoRendererInterface, +- public QGstreamerSyncMessageFilter ++ public QGstreamerSyncMessageFilter, ++ private QGstreamerBufferProbe + { + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter) +@@ -101,10 +103,10 @@ signals: + void readyChanged(bool); + + private slots: +- void updateNativeVideoSize(); ++ void updateNativeVideoSize(const QSize &size); + + private: +- static void padBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); ++ void probeCaps(GstCaps *caps); + + GstElement *m_videoSink; + WId m_windowId; +@@ -113,7 +115,6 @@ private: + bool m_fullScreen; + QSize m_nativeSize; + mutable QColor m_colorKey; +- int m_bufferProbeId; + }; + + QT_END_NAMESPACE +diff --git a/src/multimedia/gsttools_headers/qgstutils_p.h b/src/multimedia/gsttools_headers/qgstutils_p.h +index 65ff759..71a0a57 100644 +--- a/src/multimedia/gsttools_headers/qgstutils_p.h ++++ b/src/multimedia/gsttools_headers/qgstutils_p.h +@@ -49,14 +49,32 @@ + #include + #include + #include ++#include + #include + #include ++#include ++#include ++#include ++ ++#if GST_CHECK_VERSION(1,0,0) ++# 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" ++#else ++# define QT_GSTREAMER_PLAYBIN_ELEMENT_NAME "playbin2" ++# define QT_GSTREAMER_CAMERABIN_ELEMENT_NAME "camerabin2" ++# define QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME "ffmpegcolorspace" ++# define QT_GSTREAMER_RAW_AUDIO_MIME "audio/x-raw-int" ++#endif + + QT_BEGIN_NAMESPACE + + class QSize; + class QVariant; + class QByteArray; ++class QImage; ++class QVideoSurfaceFormat; + + namespace QGstUtils { + struct CameraInfo +@@ -73,8 +91,12 @@ namespace QGstUtils { + QSize capsResolution(const GstCaps *caps); + QSize capsCorrectedResolution(const GstCaps *caps); + QAudioFormat audioFormatForCaps(const GstCaps *caps); ++#if GST_CHECK_VERSION(1,0,0) ++ QAudioFormat audioFormatForSample(GstSample *sample); ++#else + QAudioFormat audioFormatForBuffer(GstBuffer *buffer); +- GstCaps *capsForAudioFormat(QAudioFormat format); ++#endif ++ GstCaps *capsForAudioFormat(const QAudioFormat &format); + void initializeGst(); + QMultimedia::SupportEstimate hasSupport(const QString &mimeType, + const QStringList &codecs, +@@ -86,9 +108,40 @@ namespace QGstUtils { + QCamera::Position cameraPosition(const QString &device, GstElementFactory * factory = 0); + int cameraOrientation(const QString &device, GstElementFactory * factory = 0); + QByteArray cameraDriver(const QString &device, GstElementFactory * factory = 0); ++ ++ QSet supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)); ++ ++#if GST_CHECK_VERSION(1,0,0) ++ QImage bufferToImage(GstBuffer *buffer, const GstVideoInfo &info); ++ QVideoSurfaceFormat formatForCaps( ++ GstCaps *caps, ++ GstVideoInfo *info, ++ QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); ++#else ++ QImage bufferToImage(GstBuffer *buffer); ++ QVideoSurfaceFormat formatForCaps( ++ GstCaps *caps, ++ int *bytesPerLine = 0, ++ QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); ++#endif ++ ++ GstCaps *capsForFormats(const QList &formats); ++ void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); ++ ++ void setMetaData(GstElement *element, const QMap &data); ++ void setMetaData(GstBin *bin, const QMap &data); ++ ++ GstCaps *videoFilterCaps(); ++ + } + + void qt_gst_object_ref_sink(gpointer object); ++GstCaps *qt_gst_pad_get_current_caps(GstPad *pad); ++GstStructure *qt_gst_structure_new_empty(const char *name); ++gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur); ++gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur); ++ ++QDebug operator <<(QDebug debug, GstCaps *caps); + + QT_END_NAMESPACE + +diff --git a/src/multimedia/gsttools_headers/qgstvideobuffer_p.h b/src/multimedia/gsttools_headers/qgstvideobuffer_p.h +index 1e0fda8..00aca48 100644 +--- a/src/multimedia/gsttools_headers/qgstvideobuffer_p.h ++++ b/src/multimedia/gsttools_headers/qgstvideobuffer_p.h +@@ -49,26 +49,47 @@ + #include + + #include ++#include + + QT_BEGIN_NAMESPACE + ++#if GST_CHECK_VERSION(1,0,0) ++class QGstVideoBuffer : public QAbstractPlanarVideoBuffer ++{ ++public: ++ QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info); ++ QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, ++ HandleType handleType, const QVariant &handle); ++#else + class QGstVideoBuffer : public QAbstractVideoBuffer + { + public: + QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine); + QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, + HandleType handleType, const QVariant &handle); ++#endif ++ + ~QGstVideoBuffer(); + + MapMode mapMode() const; + ++#if GST_CHECK_VERSION(1,0,0) ++ int map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]); ++#else + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); ++#endif ++ + void unmap(); + + QVariant handle() const { return m_handle; } + private: +- GstBuffer *m_buffer; ++#if GST_CHECK_VERSION(1,0,0) ++ GstVideoInfo m_videoInfo; ++ GstVideoFrame m_frame; ++#else + int m_bytesPerLine; ++#endif ++ GstBuffer *m_buffer; + MapMode m_mode; + QVariant m_handle; + }; +diff --git a/src/multimedia/gsttools_headers/qgstvideorendererplugin_p.h b/src/multimedia/gsttools_headers/qgstvideorendererplugin_p.h +new file mode 100644 +index 0000000..616677a +--- /dev/null ++++ b/src/multimedia/gsttools_headers/qgstvideorendererplugin_p.h +@@ -0,0 +1,111 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Jolla Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 2.1 requirements ++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3.0 as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 3.0 requirements will be ++** met: http://www.gnu.org/copyleft/gpl.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 ++ ++QT_BEGIN_NAMESPACE ++ ++class QAbstractVideoSurface; ++ ++const QLatin1String QGstVideoRendererPluginKey("gstvideorenderer"); ++ ++class 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 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 QGstVideoRendererPlugin : public QObject, public QGstVideoRendererInterface ++{ ++ Q_OBJECT ++ Q_INTERFACES(QGstVideoRendererInterface) ++public: ++ explicit QGstVideoRendererPlugin(QObject *parent = 0); ++ virtual ~QGstVideoRendererPlugin() {} ++ ++ virtual QGstVideoRenderer *createRenderer() = 0; ++ ++}; ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h +new file mode 100644 +index 0000000..6feb371 +--- /dev/null ++++ b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h +@@ -0,0 +1,183 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2014 Jolla Ltd. ++** Contact: http://www.qt-project.org/legal ++** ++** 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 Digia. For licensing terms and ++** conditions see http://qt.digia.com/licensing. For further information ++** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ++** Foundation and appearing in the file LICENSE.LGPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU Lesser General Public License version 2.1 requirements ++** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ++** ++** In addition, as a special exception, Digia gives you certain additional ++** rights. These rights are described in the Digia Qt LGPL Exception ++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ++** ++** GNU General Public License Usage ++** Alternatively, this file may be used under the terms of the GNU ++** General Public License version 3.0 as published by the Free Software ++** Foundation and appearing in the file LICENSE.GPL included in the ++** packaging of this file. Please review the following information to ++** ensure the GNU General Public License version 3.0 requirements will be ++** met: http://www.gnu.org/copyleft/gpl.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 "qgstvideorendererplugin_p.h" ++ ++#include "qgstvideorendererplugin_p.h" ++ ++QT_BEGIN_NAMESPACE ++class QAbstractVideoSurface; ++ ++class QGstDefaultVideoRenderer : public QGstVideoRenderer ++{ ++public: ++ QGstDefaultVideoRenderer(); ++ ~QGstDefaultVideoRenderer(); ++ ++ GstCaps *getCaps(QAbstractVideoSurface *surface); ++ bool start(QAbstractVideoSurface *surface, GstCaps *caps); ++ void stop(QAbstractVideoSurface *surface); ++ ++ bool proposeAllocation(GstQuery *query); ++ ++ bool present(QAbstractVideoSurface *surface, GstBuffer *buffer); ++ void flush(QAbstractVideoSurface *surface); ++ ++private: ++ QVideoSurfaceFormat m_format; ++ GstVideoInfo m_videoInfo; ++ bool m_flushed; ++}; ++ ++class QVideoSurfaceGstDelegate : public QObject ++{ ++ Q_OBJECT ++public: ++ QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface); ++ ~QVideoSurfaceGstDelegate(); ++ ++ GstCaps *caps(); ++ ++ bool start(GstCaps *caps); ++ void stop(); ++ bool proposeAllocation(GstQuery *query); ++ ++ void flush(); ++ ++ GstFlowReturn render(GstBuffer *buffer, bool show); ++ ++ bool event(QEvent *event); ++ ++ static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); ++ ++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; ++ QList m_renderers; ++ QGstVideoRenderer *m_renderer; ++ QGstVideoRenderer *m_activeRenderer; ++ ++ GstCaps *m_surfaceCaps; ++ GstCaps *m_startCaps; ++ GstBuffer *m_lastBuffer; ++ ++ bool m_notified; ++ bool m_stop; ++ bool m_render; ++ bool m_flush; ++}; ++ ++class QGstVideoRendererSink ++{ ++public: ++ GstVideoSink parent; ++ ++ static QGstVideoRendererSink *createSink(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 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 GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer); ++ static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); ++ ++private: ++ QVideoSurfaceGstDelegate *delegate; ++}; ++ ++ ++class QGstVideoRendererSinkClass ++{ ++public: ++ GstVideoSinkClass parent_class; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h +index 11b305d..0ea18c0 100644 +--- a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h ++++ b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h +@@ -45,6 +45,18 @@ + // We mean it. + // + ++#include ++ ++#if GST_CHECK_VERSION(1,0,0) ++ ++#include "qgstvideorenderersink_p.h" ++ ++QT_BEGIN_NAMESPACE ++typedef QGstVideoRendererSink QVideoSurfaceGstSink; ++QT_END_NAMESPACE ++ ++#else ++ + #include + + #include +@@ -116,10 +128,6 @@ public: + GstVideoSink parent; + + static QVideoSurfaceGstSink *createSink(QAbstractVideoSurface *surface); +- static QVideoSurfaceFormat formatForCaps(GstCaps *caps, +- int *bytesPerLine = 0, +- QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); +- static void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); + + private: + static GType get_type(); +@@ -150,7 +158,6 @@ private: + QVideoSurfaceFormat *lastSurfaceFormat; + }; + +- + class QVideoSurfaceGstSinkClass + { + public: +@@ -160,3 +167,5 @@ public: + QT_END_NAMESPACE + + #endif ++ ++#endif +diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro +index b3bdaa8..ff47768 100644 +--- a/src/multimedia/multimedia.pro ++++ b/src/multimedia/multimedia.pro +@@ -4,6 +4,8 @@ QT = core-private network gui-private + MODULE_PLUGIN_TYPES = \ + mediaservice \ + audio \ ++ video/bufferpool \ ++ video/gstvideorenderer \ + video/videonode \ + playlistformats + +diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp +index 3098aab..befbb9a 100644 +--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp ++++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp +@@ -68,89 +68,16 @@ QMultimedia::SupportEstimate QGstreamerAudioDecoderServicePlugin::hasSupport(con + return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet); + } + +-void QGstreamerAudioDecoderServicePlugin::updateSupportedMimeTypes() const ++static bool isDecoderOrDemuxer(GstElementFactory *factory) + { +- //enumerate supported mime types +- gst_init(NULL, NULL); +- +- GList *plugins, *orig_plugins; +- orig_plugins = plugins = gst_default_registry_get_plugin_list (); +- +- while (plugins) { +- GList *features, *orig_features; +- +- GstPlugin *plugin = (GstPlugin *) (plugins->data); +- plugins = g_list_next (plugins); +- +- if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED +- continue; +- +- orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get_default (), +- plugin->desc.name); +- while (features) { +- if (!G_UNLIKELY(features->data == NULL)) { +- GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); +- if (GST_IS_ELEMENT_FACTORY (feature)) { +- GstElementFactory *factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); +- if (factory +- && factory->numpadtemplates > 0 +- && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0 +- || qstrcmp(factory->details.klass, "Codec/Demux") == 0 )) { +- const GList *pads = factory->staticpadtemplates; +- while (pads) { +- GstStaticPadTemplate *padtemplate = (GstStaticPadTemplate*)(pads->data); +- pads = g_list_next (pads); +- if (padtemplate->direction != GST_PAD_SINK) +- continue; +- if (padtemplate->static_caps.string) { +- GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); +- if (!gst_caps_is_any (caps) && ! gst_caps_is_empty (caps)) { +- for (guint i = 0; i < gst_caps_get_size(caps); i++) { +- GstStructure *structure = gst_caps_get_structure(caps, i); +- QString nameLowcase = QString(gst_structure_get_name (structure)).toLower(); +- +- m_supportedMimeTypeSet.insert(nameLowcase); +- if (nameLowcase.contains("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(str); +- QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); +- foreach (const QString &e, elements) +- m_supportedMimeTypeSet.insert(nameLowcase + e); +- g_free (str); +- } +- } +- } +- } +- gst_caps_unref(caps); +- } +- } +- gst_object_unref (factory); +- } +- } else if (GST_IS_TYPE_FIND_FACTORY(feature)) { +- QString name(gst_plugin_feature_get_name(feature)); +- if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type +- m_supportedMimeTypeSet.insert(name.toLower()); +- } +- } +- features = g_list_next (features); +- } +- gst_plugin_feature_list_free (orig_features); +- } +- gst_plugin_list_free (orig_plugins); ++ return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DEMUXER) ++ || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DECODER ++ | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO); ++} + +-#if defined QT_SUPPORTEDMIMETYPES_DEBUG +- QStringList list = m_supportedMimeTypeSet.toList(); +- list.sort(); +- if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { +- foreach (const QString &type, list) +- qDebug() << type; +- } +-#endif ++void QGstreamerAudioDecoderServicePlugin::updateSupportedMimeTypes() const ++{ ++ m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isDecoderOrDemuxer); + } + + QStringList QGstreamerAudioDecoderServicePlugin::supportedMimeTypes() const +diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp +index f944a60..69876b9 100644 +--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp ++++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp +@@ -85,7 +85,7 @@ QGstreamerAudioDecoderSession::QGstreamerAudioDecoderSession(QObject *parent) + m_durationQueries(0) + { + // Create pipeline here +- m_playbin = gst_element_factory_make("playbin2", NULL); ++ m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, NULL); + + if (m_playbin != 0) { + // Sort out messages +@@ -446,21 +446,40 @@ QAudioBuffer QGstreamerAudioDecoderSession::read() + if (buffersAvailable == 1) + emit bufferAvailableChanged(false); + ++ const char* bufferData = 0; ++ int bufferSize = 0; ++ ++#if GST_CHECK_VERSION(1,0,0) ++ GstSample *sample = gst_app_sink_pull_sample(m_appSink); ++ GstBuffer *buffer = gst_sample_get_buffer(sample); ++ GstMapInfo mapInfo; ++ gst_buffer_map(buffer, &mapInfo, GST_MAP_READ); ++ bufferData = (const char*)mapInfo.data; ++ bufferSize = mapInfo.size; ++ QAudioFormat format = QGstUtils::audioFormatForSample(sample); ++#else + GstBuffer *buffer = gst_app_sink_pull_buffer(m_appSink); +- ++ bufferData = (const char*)buffer->data; ++ bufferSize = buffer->size; + QAudioFormat format = QGstUtils::audioFormatForBuffer(buffer); ++#endif ++ + if (format.isValid()) { + // XXX At the moment we have to copy data from GstBuffer into QAudioBuffer. + // We could improve performance by implementing QAbstractAudioBuffer for GstBuffer. + qint64 position = getPositionFromBuffer(buffer); +- audioBuffer = QAudioBuffer(QByteArray((const char*)buffer->data, buffer->size), format, position); ++ audioBuffer = QAudioBuffer(QByteArray((const char*)bufferData, bufferSize), format, position); + position /= 1000; // convert to milliseconds + if (position != m_position) { + m_position = position; + emit positionChanged(m_position); + } + } ++#if GST_CHECK_VERSION(1,0,0) ++ gst_sample_unref(sample); ++#else + gst_buffer_unref(buffer); ++#endif + } + + return audioBuffer; +@@ -488,7 +507,7 @@ void QGstreamerAudioDecoderSession::processInvalidMedia(QAudioDecoder::Error err + emit error(int(errorCode), errorString); + } + +-GstFlowReturn QGstreamerAudioDecoderSession::new_buffer(GstAppSink *, gpointer user_data) ++GstFlowReturn QGstreamerAudioDecoderSession::new_sample(GstAppSink *, gpointer user_data) + { + // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()." + QGstreamerAudioDecoderSession *session = reinterpret_cast(user_data); +@@ -531,7 +550,11 @@ void QGstreamerAudioDecoderSession::addAppSink() + + GstAppSinkCallbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); +- callbacks.new_buffer = &new_buffer; ++#if GST_CHECK_VERSION(1,0,0) ++ callbacks.new_sample = &new_sample; ++#else ++ callbacks.new_buffer = &new_sample; ++#endif + gst_app_sink_set_callbacks(m_appSink, &callbacks, this, NULL); + gst_app_sink_set_max_buffers(m_appSink, MAX_BUFFERS_IN_QUEUE); + gst_base_sink_set_sync(GST_BASE_SINK(m_appSink), FALSE); +@@ -553,11 +576,10 @@ void QGstreamerAudioDecoderSession::removeAppSink() + + void QGstreamerAudioDecoderSession::updateDuration() + { +- GstFormat format = GST_FORMAT_TIME; + gint64 gstDuration = 0; + int duration = -1; + +- if (m_playbin && gst_element_query_duration(m_playbin, &format, &gstDuration)) ++ if (m_playbin && qt_gst_element_query_duration(m_playbin, GST_FORMAT_TIME, &gstDuration)) + duration = gstDuration / 1000000; + + if (m_duration != duration) { +diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h +index 0912196..068221c 100644 +--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h ++++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h +@@ -92,7 +92,7 @@ public: + qint64 position() const; + qint64 duration() const; + +- static GstFlowReturn new_buffer(GstAppSink *sink, gpointer user_data); ++ static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data); + + signals: + void stateChanged(QAudioDecoder::State newState); +diff --git a/src/plugins/gstreamer/camerabin/camerabin.pro b/src/plugins/gstreamer/camerabin/camerabin.pro +index bba797f..64fee3e 100644 +--- a/src/plugins/gstreamer/camerabin/camerabin.pro ++++ b/src/plugins/gstreamer/camerabin/camerabin.pro +@@ -79,7 +79,7 @@ config_gstreamer_photography { + $$PWD/camerabinlocks.cpp \ + $$PWD/camerabinzoom.cpp + +- LIBS += -lgstphotography-0.10 ++ LIBS += -lgstphotography-$$GST_VERSION + DEFINES += GST_USE_UNSTABLE_API #prevents warnings because of unstable photography API + } + +diff --git a/src/plugins/gstreamer/camerabin/camerabincontainer.cpp b/src/plugins/gstreamer/camerabin/camerabincontainer.cpp +index ebb914b..9531f01 100644 +--- a/src/plugins/gstreamer/camerabin/camerabincontainer.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabincontainer.cpp +@@ -96,7 +96,7 @@ GstEncodingContainerProfile *CameraBinContainer::createProfile() + GstCaps *caps; + + if (m_actualFormat.isEmpty()) { +- caps = gst_caps_new_any(); ++ return 0; + } else { + QString format = m_actualFormat; + QStringList supportedFormats = m_supportedContainers.supportedCodecs(); +diff --git a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp +index 3ec9927..8c6b8b0 100644 +--- a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp +@@ -95,11 +95,6 @@ void CameraBinControl::setCaptureMode(QCamera::CaptureModes mode) + captureMode() == QCamera::CaptureStillImage ? + CamerabinResourcePolicy::ImageCaptureResources : + CamerabinResourcePolicy::VideoCaptureResources); +-#if (GST_VERSION_MAJOR == 0) && ((GST_VERSION_MINOR < 10) || (GST_VERSION_MICRO < 23)) +- //due to bug in v4l2src, it's necessary to reload camera on video caps changes +- //https://bugzilla.gnome.org/show_bug.cgi?id=649832 +- reloadLater(); +-#endif + } + emit captureModeChanged(mode); + } +@@ -299,6 +294,8 @@ bool CameraBinControl::canChangeProperty(PropertyChangeType changeType, QCamera: + + switch (changeType) { + case QCameraControl::CaptureMode: ++ return status != QCamera::ActiveStatus; ++ break; + case QCameraControl::ImageEncodingSettings: + case QCameraControl::VideoEncodingSettings: + case QCameraControl::Viewfinder: +diff --git a/src/plugins/gstreamer/camerabin/camerabinexposure.cpp b/src/plugins/gstreamer/camerabin/camerabinexposure.cpp +index a235de2..795fd42 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinexposure.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinexposure.cpp +@@ -37,6 +37,10 @@ + + #include + ++#if !GST_CHECK_VERSION(1,0,0) ++typedef GstSceneMode GstPhotographySceneMode; ++#endif ++ + QT_BEGIN_NAMESPACE + + CameraBinExposure::CameraBinExposure(CameraBinSession *session) +@@ -119,7 +123,7 @@ QVariant CameraBinExposure::actualValue(ExposureParameter parameter) const + } + case QCameraExposureControl::ExposureMode: + { +- GstSceneMode sceneMode; ++ GstPhotographySceneMode sceneMode; + gst_photography_get_scene_mode(m_session->photography(), &sceneMode); + + switch (sceneMode) { +@@ -167,7 +171,7 @@ bool CameraBinExposure::setValue(ExposureParameter parameter, const QVariant& va + case QCameraExposureControl::ExposureMode: + { + QCameraExposure::ExposureMode mode = QCameraExposure::ExposureMode(value.toInt()); +- GstSceneMode sceneMode; ++ GstPhotographySceneMode sceneMode; + gst_photography_get_scene_mode(m_session->photography(), &sceneMode); + + switch (mode) { +diff --git a/src/plugins/gstreamer/camerabin/camerabinflash.cpp b/src/plugins/gstreamer/camerabin/camerabinflash.cpp +index 2140f66..51bb9a2 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinflash.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinflash.cpp +@@ -37,6 +37,10 @@ + + #include + ++#if !GST_CHECK_VERSION(1,0,0) ++typedef GstFlashMode GstPhotographyFlashMode; ++#endif ++ + QT_BEGIN_NAMESPACE + + CameraBinFlash::CameraBinFlash(CameraBinSession *session) +@@ -51,7 +55,7 @@ CameraBinFlash::~CameraBinFlash() + + QCameraExposure::FlashModes CameraBinFlash::flashMode() const + { +- GstFlashMode flashMode; ++ GstPhotographyFlashMode flashMode; + gst_photography_get_flash_mode(m_session->photography(), &flashMode); + + QCameraExposure::FlashModes modes; +@@ -70,7 +74,7 @@ QCameraExposure::FlashModes CameraBinFlash::flashMode() const + + void CameraBinFlash::setFlashMode(QCameraExposure::FlashModes mode) + { +- GstFlashMode flashMode; ++ GstPhotographyFlashMode flashMode; + gst_photography_get_flash_mode(m_session->photography(), &flashMode); + + if (mode.testFlag(QCameraExposure::FlashAuto)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO; +diff --git a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp +index 665e204..061c680 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp +@@ -39,6 +39,12 @@ + #include + #include + ++#include ++ ++#if !GST_CHECK_VERSION(1,0,0) ++typedef GstFocusMode GstPhotographyFocusMode; ++#endif ++ + //#define CAMERABIN_DEBUG 1 + + QT_BEGIN_NAMESPACE +@@ -73,7 +79,7 @@ QCameraFocus::FocusModes CameraBinFocus::focusMode() const + + void CameraBinFocus::setFocusMode(QCameraFocus::FocusModes mode) + { +- GstFocusMode photographyMode; ++ GstPhotographyFocusMode photographyMode; + + switch (mode) { + case QCameraFocus::AutoFocus: +@@ -181,9 +187,10 @@ QCameraFocusZoneList CameraBinFocus::focusZones() const + void CameraBinFocus::handleFocusMessage(GstMessage *gm) + { + //it's a sync message, so it's called from non main thread +- if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) { ++ const GstStructure *structure = gst_message_get_structure(gm); ++ if (gst_structure_has_name(structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) { + gint status = GST_PHOTOGRAPHY_FOCUS_STATUS_NONE; +- gst_structure_get_int (gm->structure, "status", &status); ++ gst_structure_get_int (structure, "status", &status); + QCamera::LockStatus focusStatus = m_focusStatus; + QCamera::LockChangeReason reason = QCamera::UserRequest; + +@@ -243,7 +250,7 @@ void CameraBinFocus::_q_handleCameraStateChange(QCamera::State state) + m_cameraState = state; + if (state == QCamera::ActiveState) { + if (GstPad *pad = gst_element_get_static_pad(m_session->cameraSource(), "vfsrc")) { +- if (GstCaps *caps = gst_pad_get_negotiated_caps(pad)) { ++ if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) { + if (GstStructure *structure = gst_caps_get_structure(caps, 0)) { + int width = 0; + int height = 0; +diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp +index 6952155..8b51306 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp +@@ -53,11 +53,13 @@ QT_BEGIN_NAMESPACE + + CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session) + :QCameraImageCaptureControl(session) ++ , m_encoderProbe(this) ++ , m_muxerProbe(this) + , m_session(session) +- , m_ready(false) +- , m_requestId(0) + , m_jpegEncoderElement(0) + , m_metadataMuxerElement(0) ++ , m_requestId(0) ++ , m_ready(false) + { + connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState())); + connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int))); +@@ -108,11 +110,18 @@ void CameraBinImageCapture::updateState() + } + } + +-gboolean CameraBinImageCapture::metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *self) ++#if GST_CHECK_VERSION(1,0,0) ++GstPadProbeReturn CameraBinImageCapture::encoderEventProbe( ++ GstPad *, GstPadProbeInfo *info, gpointer user_data) + { +- Q_UNUSED(pad); +- +- if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { ++ GstEvent * const event = gst_pad_probe_info_get_event(info); ++#else ++gboolean CameraBinImageCapture::encoderEventProbe( ++ GstElement *, GstEvent *event, gpointer user_data) ++{ ++#endif ++ CameraBinImageCapture * const self = static_cast(user_data); ++ if (event && GST_EVENT_TYPE(event) == GST_EVENT_TAG) { + GstTagList *gstTags; + gst_event_parse_tag(event, &gstTags); + QMap extendedTags = QGstUtils::gstTagListToMap(gstTags); +@@ -146,17 +155,31 @@ gboolean CameraBinImageCapture::metadataEventProbe(GstPad *pad, GstEvent *event, + } + } + } ++#if GST_CHECK_VERSION(1,0,0) ++ return GST_PAD_PROBE_OK; ++#else ++ return TRUE; ++#endif ++} + +- return true; ++void CameraBinImageCapture::EncoderProbe::probeCaps(GstCaps *caps) ++{ ++#if GST_CHECK_VERSION(1,0,0) ++ capture->m_bufferFormat = QGstUtils::formatForCaps(caps, &capture->m_videoInfo); ++#else ++ int bytesPerLine = 0; ++ QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine); ++ capture->m_bytesPerLine = bytesPerLine; ++ capture->m_bufferFormat = format; ++#endif + } + +-gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self) ++bool CameraBinImageCapture::EncoderProbe::probeBuffer(GstBuffer *buffer) + { +- Q_UNUSED(pad); +- CameraBinSession *session = self->m_session; ++ CameraBinSession * const session = capture->m_session; + + #ifdef DEBUG_CAPTURE +- qDebug() << "Uncompressed buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer)); ++ qDebug() << "Uncompressed buffer probe"; + #endif + + QCameraImageCapture::CaptureDestinations destination = +@@ -165,21 +188,23 @@ gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer * + + if (destination & QCameraImageCapture::CaptureToBuffer) { + if (format != QVideoFrame::Format_Jpeg) { +- GstCaps *caps = GST_BUFFER_CAPS(buffer); +- int bytesPerLine = -1; +- QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine); + #ifdef DEBUG_CAPTURE + qDebug() << "imageAvailable(uncompressed):" << format; + #endif +- QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, bytesPerLine); ++#if GST_CHECK_VERSION(1,0,0) ++ QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, capture->m_videoInfo); ++#else ++ QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, capture->m_bytesPerLine); ++#endif + +- QVideoFrame frame(videoBuffer, +- format.frameSize(), +- format.pixelFormat()); ++ QVideoFrame frame( ++ videoBuffer, ++ capture->m_bufferFormat.frameSize(), ++ capture->m_bufferFormat.pixelFormat()); + +- QMetaObject::invokeMethod(self, "imageAvailable", ++ QMetaObject::invokeMethod(capture, "imageAvailable", + Qt::QueuedConnection, +- Q_ARG(int, self->m_requestId), ++ Q_ARG(int, capture->m_requestId), + Q_ARG(QVideoFrame, frame)); + } + } +@@ -192,25 +217,40 @@ gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer * + return keepBuffer; + } + +-gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self) ++void CameraBinImageCapture::MuxerProbe::probeCaps(GstCaps *caps) + { +- Q_UNUSED(pad); +- CameraBinSession *session = self->m_session; ++ capture->m_jpegResolution = QGstUtils::capsCorrectedResolution(caps); ++} + +-#ifdef DEBUG_CAPTURE +- qDebug() << "Jpeg buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer)); +-#endif ++bool CameraBinImageCapture::MuxerProbe::probeBuffer(GstBuffer *buffer) ++{ ++ CameraBinSession * const session = capture->m_session; + + QCameraImageCapture::CaptureDestinations destination = + session->captureDestinationControl()->captureDestination(); + + if ((destination & QCameraImageCapture::CaptureToBuffer) && + session->captureBufferFormatControl()->bufferFormat() == QVideoFrame::Format_Jpeg) { +- QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, +- -1); //bytesPerLine is not available for jpegs + +- QSize resolution = QGstUtils::capsCorrectedResolution(GST_BUFFER_CAPS(buffer)); ++ QSize resolution = capture->m_jpegResolution; + //if resolution is not presented in caps, try to find it from encoded jpeg data: ++#if GST_CHECK_VERSION(1,0,0) ++ GstMapInfo mapInfo; ++ if (resolution.isEmpty() && gst_buffer_map(buffer, &mapInfo, GST_MAP_READ)) { ++ QBuffer data; ++ data.setData(reinterpret_cast(mapInfo.data), mapInfo.size); ++ ++ QImageReader reader(&data, "JPEG"); ++ resolution = reader.size(); ++ ++ gst_buffer_unmap(buffer, &mapInfo); ++ } ++ ++ GstVideoInfo info; ++ gst_video_info_set_format( ++ &info, GST_VIDEO_FORMAT_ENCODED, resolution.width(), resolution.height()); ++ QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, info); ++#else + if (resolution.isEmpty()) { + QBuffer data; + data.setData(reinterpret_cast(GST_BUFFER_DATA(buffer)), GST_BUFFER_SIZE(buffer)); +@@ -218,20 +258,28 @@ gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, + resolution = reader.size(); + } + ++ QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, ++ -1); //bytesPerLine is not available for jpegs ++#endif ++ ++ + QVideoFrame frame(videoBuffer, + resolution, + QVideoFrame::Format_Jpeg); +- +- QMetaObject::invokeMethod(self, "imageAvailable", ++ QMetaObject::invokeMethod(capture, "imageAvailable", + Qt::QueuedConnection, +- Q_ARG(int, self->m_requestId), ++ Q_ARG(int, capture->m_requestId), + Q_ARG(QVideoFrame, frame)); + } + +- //drop the buffer if capture to file was disabled +- return destination & QCameraImageCapture::CaptureToFile; ++ ++ // Theoretically we could drop the buffer here when don't want to capture to file but that ++ // prevents camerabin from recognizing that capture has been completed and returning ++ // to its idle state. ++ return true; + } + ++ + bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) + { + //Install metadata event and buffer probes +@@ -252,9 +300,10 @@ bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) + return false; + + QString elementName = QString::fromLatin1(gst_element_get_name(element)); ++#if !GST_CHECK_VERSION(1,0,0) + GstElementClass *elementClass = GST_ELEMENT_GET_CLASS(element); + QString elementLongName = elementClass->details.longname; +- ++#endif + if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) { + m_jpegEncoderElement = element; + GstPad *sinkpad = gst_element_get_static_pad(element, "sink"); +@@ -264,21 +313,23 @@ bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) + #ifdef DEBUG_CAPTURE + qDebug() << "install metadata probe"; + #endif +- gst_pad_add_event_probe(sinkpad, +- G_CALLBACK(CameraBinImageCapture::metadataEventProbe), +- this); +- ++#if GST_CHECK_VERSION(1,0,0) ++ gst_pad_add_probe( ++ sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, encoderEventProbe, this, NULL); ++#else ++ gst_pad_add_event_probe(sinkpad, G_CALLBACK(encoderEventProbe), this); ++#endif + #ifdef DEBUG_CAPTURE + qDebug() << "install uncompressed buffer probe"; + #endif +- gst_pad_add_buffer_probe(sinkpad, +- G_CALLBACK(CameraBinImageCapture::uncompressedBufferProbe), +- this); ++ m_encoderProbe.addProbeToPad(sinkpad, true); + + gst_object_unref(sinkpad); +- } else if ((elementName.contains("jifmux") || +- elementName.startsWith("metadatamux") || +- elementLongName == QLatin1String("JPEG stream muxer")) ++ } else if ((elementName.contains("jifmux") ++#if !GST_CHECK_VERSION(1,0,0) ++ || elementLongName == QLatin1String("JPEG stream muxer") ++#endif ++ || elementName.startsWith("metadatamux")) + && element != m_metadataMuxerElement) { + //Jpeg encoded buffer probe is added after jifmux/metadatamux + //element to ensure the resulting jpeg buffer contains capture metadata +@@ -288,9 +339,8 @@ bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) + #ifdef DEBUG_CAPTURE + qDebug() << "install jpeg buffer probe"; + #endif +- gst_pad_add_buffer_probe(srcpad, +- G_CALLBACK(CameraBinImageCapture::jpegBufferProbe), +- this); ++ m_muxerProbe.addProbeToPad(srcpad); ++ + gst_object_unref(srcpad); + } + } +diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.h b/src/plugins/gstreamer/camerabin/camerabinimagecapture.h +index c2e26f5..9a52dd9 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinimagecapture.h ++++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.h +@@ -38,6 +38,14 @@ + #include + #include "camerabinsession.h" + ++#include ++ ++#include ++ ++#if GST_CHECK_VERSION(1,0,0) ++#include ++#endif ++ + QT_BEGIN_NAMESPACE + + class CameraBinImageCapture : public QCameraImageCaptureControl, public QGstreamerBusMessageFilter +@@ -61,15 +69,47 @@ private slots: + void updateState(); + + private: +- static gboolean metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *); +- static gboolean uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); +- static gboolean jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); ++#if GST_CHECK_VERSION(1,0,0) ++ static GstPadProbeReturn encoderEventProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data); ++#else ++ static gboolean encoderEventProbe(GstElement *, GstEvent *event, gpointer user_data); ++#endif ++ ++ class EncoderProbe : public QGstreamerBufferProbe ++ { ++ public: ++ EncoderProbe(CameraBinImageCapture *capture) : capture(capture) {} ++ void probeCaps(GstCaps *caps); ++ bool probeBuffer(GstBuffer *buffer); ++ ++ private: ++ CameraBinImageCapture * const capture; ++ } m_encoderProbe; ++ ++ class MuxerProbe : public QGstreamerBufferProbe ++ { ++ public: ++ MuxerProbe(CameraBinImageCapture *capture) : capture(capture) {} ++ void probeCaps(GstCaps *caps); ++ bool probeBuffer(GstBuffer *buffer); + ++ private: ++ CameraBinImageCapture * const capture; ++ ++ } m_muxerProbe; ++ ++ QVideoSurfaceFormat m_bufferFormat; ++ QSize m_jpegResolution; + CameraBinSession *m_session; +- bool m_ready; +- int m_requestId; + GstElement *m_jpegEncoderElement; + GstElement *m_metadataMuxerElement; ++#if GST_CHECK_VERSION(1,0,0) ++ GstVideoInfo m_videoInfo; ++#else ++ int m_bytesPerLine; ++#endif ++ int m_requestId; ++ bool m_ready; + }; + + QT_END_NAMESPACE +diff --git a/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp +index 824f996..739364f 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp +@@ -49,7 +49,6 @@ CameraBinImageEncoder::~CameraBinImageEncoder() + + QList CameraBinImageEncoder::supportedResolutions(const QImageEncoderSettings &, bool *continuous) const + { +- qDebug() << "CameraBinImageEncoder::supportedResolutions()"; + if (continuous) + *continuous = false; + +diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp +index ebfb087..811225f 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp +@@ -34,7 +34,11 @@ + #include "camerabinimageprocessing.h" + #include "camerabinsession.h" + +-#include ++#if GST_CHECK_VERSION(1,0,0) ++# include ++#else ++# include ++#endif + + QT_BEGIN_NAMESPACE + +@@ -126,7 +130,7 @@ bool CameraBinImageProcessing::setColorBalanceValue(const QString& channel, qrea + QCameraImageProcessing::WhiteBalanceMode CameraBinImageProcessing::whiteBalanceMode() const + { + #ifdef HAVE_GST_PHOTOGRAPHY +- GstWhiteBalanceMode wbMode; ++ GstPhotographyWhiteBalanceMode wbMode; + gst_photography_get_white_balance_mode(m_session->photography(), &wbMode); + return m_mappedWbValues[wbMode]; + #else +diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h +index dcefcd0..2c6347f 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h ++++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h +@@ -41,7 +41,10 @@ + #include + + #ifdef HAVE_GST_PHOTOGRAPHY +-#include ++# include ++# if !GST_CHECK_VERSION(1,0,0) ++typedef GstWhiteBalanceMode GstPhotographyWhiteBalanceMode; ++# endif + #endif + + QT_BEGIN_NAMESPACE +@@ -73,7 +76,7 @@ private: + CameraBinSession *m_session; + QMap m_values; + #ifdef HAVE_GST_PHOTOGRAPHY +- QMap m_mappedWbValues; ++ QMap m_mappedWbValues; + #endif + }; + +diff --git a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp +index 5148135..bc1b260 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp +@@ -126,7 +126,7 @@ static const QGStreamerMetaDataKeys *qt_gstreamerMetaDataKeys() + metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumTitle, GST_TAG_ALBUM, QVariant::String)); + metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumArtist, GST_TAG_ARTIST, QVariant::String)); + metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ContributingArtist, GST_TAG_PERFORMER, QVariant::String)); +-#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) ++#if GST_CHECK_VERSION(0,10,19) + metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Composer, GST_TAG_COMPOSER, QVariant::String)); + #endif + //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Conductor, 0, QVariant::String)); +@@ -153,8 +153,7 @@ static const QGStreamerMetaDataKeys *qt_gstreamerMetaDataKeys() + //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Director, 0, QVariant::String)); + metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::LeadPerformer, GST_TAG_PERFORMER, QVariant::String)); + //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Writer, 0, QVariant::String)); +- +-#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 30) ++#if GST_CHECK_VERSION(0,10,30) + // Photos + metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraManufacturer, GST_TAG_DEVICE_MANUFACTURER, QVariant::String)); + metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraModel, GST_TAG_DEVICE_MODEL, QVariant::String)); +diff --git a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp +index 3a04c2f..801c7ab 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp +@@ -110,9 +110,10 @@ void CameraBinRecorder::updateStatus() + m_state = QMediaRecorder::StoppedState; + m_session->stopVideoRecording(); + } +- m_status = m_session->pendingState() == QCamera::ActiveState ? +- QMediaRecorder::LoadingStatus : +- QMediaRecorder::UnloadedStatus; ++ m_status = m_session->pendingState() == QCamera::ActiveState ++ && m_session->captureMode().testFlag(QCamera::CaptureVideo) ++ ? QMediaRecorder::LoadingStatus ++ : QMediaRecorder::UnloadedStatus; + } + + if (m_state != oldState) +@@ -161,8 +162,6 @@ void CameraBinRecorder::applySettings() + + QVideoEncoderSettings videoSettings = videoEncoderControl->videoSettings(); + videoSettings.setCodec(candidate[1]); +- if (videoSettings.resolution().isEmpty()) +- videoSettings.setResolution(640, 480); + videoEncoderControl->setActualVideoSettings(videoSettings); + + QAudioEncoderSettings audioSettings = audioEncoderControl->audioSettings(); +diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.cpp b/src/plugins/gstreamer/camerabin/camerabinservice.cpp +index 969955f..388f2fd 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinservice.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinservice.cpp +@@ -56,11 +56,11 @@ + #include "camerabincapturedestination.h" + #include "camerabinviewfindersettings.h" + #include ++#include + + #include + #include + +- + #if defined(HAVE_WIDGETS) + #include + #endif +@@ -121,7 +121,6 @@ CameraBinService::CameraBinService(GstElementFactory *sourceFactory, QObject *pa + #else + m_videoWindow = new QGstreamerVideoWindow(this); + #endif +- + #if defined(HAVE_WIDGETS) + m_videoWidgetControl = new QGstreamerVideoWidgetControl(this); + #endif +@@ -150,8 +149,6 @@ QMediaControl *CameraBinService::requestControl(const char *name) + if (!m_captureSession) + return 0; + +- //qDebug() << "Request control" << name; +- + if (!m_videoOutput) { + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + m_videoOutput = m_videoRenderer; +@@ -249,7 +246,7 @@ void CameraBinService::releaseControl(QMediaControl *control) + + bool CameraBinService::isCameraBinAvailable() + { +- GstElementFactory *factory = gst_element_factory_find("camerabin2"); ++ GstElementFactory *factory = gst_element_factory_find(QT_GSTREAMER_CAMERABIN_ELEMENT_NAME); + if (factory) { + gst_object_unref(GST_OBJECT(factory)); + return true; +diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp +index a4038c5..f916b58 100644 +--- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp ++++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp +@@ -140,8 +140,8 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa + { + if (m_sourceFactory) + gst_object_ref(GST_OBJECT(m_sourceFactory)); ++ m_camerabin = gst_element_factory_make(QT_GSTREAMER_CAMERABIN_ELEMENT_NAME, "camerabin"); + +- m_camerabin = gst_element_factory_make("camerabin2", "camerabin2"); + g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this); + g_signal_connect(G_OBJECT(m_camerabin), "element-added", G_CALLBACK(elementAdded), this); + g_signal_connect(G_OBJECT(m_camerabin), "element-removed", G_CALLBACK(elementRemoved), this); +@@ -178,7 +178,15 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa + //post image preview in RGB format + g_object_set(G_OBJECT(m_camerabin), POST_PREVIEWS_PROPERTY, TRUE, NULL); + ++#if GST_CHECK_VERSION(1,0,0) ++ GstCaps *previewCaps = gst_caps_new_simple( ++ "video/x-raw", ++ "format", G_TYPE_STRING, "RGBx", ++ NULL); ++#else + GstCaps *previewCaps = gst_caps_from_string("video/x-raw-rgb"); ++#endif ++ + g_object_set(G_OBJECT(m_camerabin), PREVIEW_CAPS_PROPERTY, previewCaps, NULL); + gst_caps_unref(previewCaps); + } +@@ -243,6 +251,7 @@ bool CameraBinSession::setupCameraBin() + qWarning() << "Staring camera without viewfinder available"; + m_viewfinderElement = gst_element_factory_make("fakesink", NULL); + } ++ g_object_set(G_OBJECT(m_viewfinderElement), "sync", FALSE, NULL); + qt_gst_object_ref_sink(GST_OBJECT(m_viewfinderElement)); + gst_element_set_state(m_camerabin, GST_STATE_NULL); + g_object_set(G_OBJECT(m_camerabin), VIEWFINDER_SINK_PROPERTY, m_viewfinderElement, NULL); +@@ -251,61 +260,27 @@ bool CameraBinSession::setupCameraBin() + return true; + } + +-static GstCaps *resolutionToCaps(const QSize &resolution, const QPair &rate = qMakePair(0,0)) ++static GstCaps *resolutionToCaps(const QSize &resolution, qreal frameRate = 0.0) + { +- if (resolution.isEmpty()) +- return gst_caps_new_any(); ++ GstCaps *caps = QGstUtils::videoFilterCaps(); + +- GstCaps *caps = 0; +- if (rate.second > 0) { +- caps = gst_caps_new_full(gst_structure_new("video/x-raw-yuv", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- "framerate", GST_TYPE_FRACTION, rate.first, rate.second, +- NULL), +- gst_structure_new("video/x-raw-rgb", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- "framerate", GST_TYPE_FRACTION, rate.first, rate.second, +- NULL), +- gst_structure_new("video/x-raw-data", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- "framerate", GST_TYPE_FRACTION, rate.first, rate.second, +- NULL), +- gst_structure_new("video/x-android-buffer", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- "framerate", GST_TYPE_FRACTION, rate.first, rate.second, +- NULL), +- gst_structure_new("image/jpeg", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- "framerate", GST_TYPE_FRACTION, rate.first, rate.second, +- NULL), +- NULL); +- } else { +- caps = gst_caps_new_full (gst_structure_new ("video/x-raw-yuv", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- NULL), +- gst_structure_new ("video/x-raw-rgb", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- NULL), +- gst_structure_new("video/x-raw-data", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- NULL), +- gst_structure_new ("video/x-android-buffer", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- NULL), +- gst_structure_new ("image/jpeg", +- "width", G_TYPE_INT, resolution.width(), +- "height", G_TYPE_INT, resolution.height(), +- NULL), +- NULL); ++ if (!resolution.isEmpty()) { ++ gst_caps_set_simple( ++ caps, ++ "width", G_TYPE_INT, resolution.width(), ++ "height", G_TYPE_INT, resolution.height(), ++ NULL); ++ } ++ ++ if (frameRate > 0.0) { ++ gint numerator; ++ gint denominator; ++ gst_util_double_to_fraction(frameRate, &numerator, &denominator); ++ ++ gst_caps_set_simple( ++ caps, ++ "framerate", GST_TYPE_FRACTION, numerator, denominator, ++ NULL); + } + + return caps; +@@ -314,40 +289,40 @@ static GstCaps *resolutionToCaps(const QSize &resolution, const QPair + void CameraBinSession::setupCaptureResolution() + { + QSize resolution = m_imageEncodeControl->imageSettings().resolution(); +- if (!resolution.isEmpty()) { ++ { + GstCaps *caps = resolutionToCaps(resolution); + #if CAMERABIN_DEBUG +- qDebug() << Q_FUNC_INFO << "set image resolution" << resolution << gst_caps_to_string(caps); ++ qDebug() << Q_FUNC_INFO << "set image resolution" << resolution << caps; + #endif + g_object_set(m_camerabin, IMAGE_CAPTURE_CAPS_PROPERTY, caps, NULL); +- gst_caps_unref(caps); +- } else { +- g_object_set(m_camerabin, IMAGE_CAPTURE_CAPS_PROPERTY, NULL, NULL); ++ if (caps) ++ gst_caps_unref(caps); + } + ++ const QSize viewfinderResolution = m_viewfinderSettingsControl->resolution(); + resolution = m_videoEncodeControl->actualVideoSettings().resolution(); +- //qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); +- if (!resolution.isEmpty()) { +- GstCaps *caps = resolutionToCaps(resolution /*, framerate*/); //convert to rational ++ qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); ++ { ++ GstCaps *caps = resolutionToCaps( ++ !resolution.isEmpty() ? resolution : viewfinderResolution, framerate); + #if CAMERABIN_DEBUG +- qDebug() << Q_FUNC_INFO << "set video resolution" << resolution << gst_caps_to_string(caps); ++ qDebug() << Q_FUNC_INFO << "set video resolution" << resolution << caps; + #endif + g_object_set(m_camerabin, VIDEO_CAPTURE_CAPS_PROPERTY, caps, NULL); +- gst_caps_unref(caps); +- } else { +- g_object_set(m_camerabin, VIDEO_CAPTURE_CAPS_PROPERTY, NULL, NULL); ++ if (caps) ++ gst_caps_unref(caps); + } + +- resolution = m_viewfinderSettingsControl->resolution(); +- if (!resolution.isEmpty()) { ++ if (!viewfinderResolution.isEmpty()) ++ resolution = viewfinderResolution; ++ { + GstCaps *caps = resolutionToCaps(resolution); + #if CAMERABIN_DEBUG +- qDebug() << Q_FUNC_INFO << "set viewfinder resolution" << resolution << gst_caps_to_string(caps); ++ qDebug() << Q_FUNC_INFO << "set viewfinder resolution" << resolution << caps; + #endif + g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, caps, NULL); +- gst_caps_unref(caps); +- } else { +- g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, NULL, NULL); ++ if (caps) ++ gst_caps_unref(caps); + } + + if (m_videoEncoder) +@@ -363,13 +338,17 @@ void CameraBinSession::setAudioCaptureCaps() + if (sampleRate == -1 && channelCount == -1) + return; + ++#if GST_CHECK_VERSION(1,0,0) ++ GstStructure *structure = gst_structure_new_empty(QT_GSTREAMER_RAW_AUDIO_MIME); ++#else + GstStructure *structure = gst_structure_new( +- "audio/x-raw-int", ++ QT_GSTREAMER_RAW_AUDIO_MIME, + "endianness", G_TYPE_INT, 1234, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, 16, + "depth", G_TYPE_INT, 16, + NULL); ++#endif + if (sampleRate != -1) + gst_structure_set(structure, "rate", G_TYPE_INT, sampleRate, NULL); + if (channelCount != -1) +@@ -760,7 +739,7 @@ qint64 CameraBinSession::duration() const + if (fileSink) { + GstFormat format = GST_FORMAT_TIME; + gint64 duration = 0; +- bool ret = gst_element_query_position(fileSink, &format, &duration); ++ bool ret = qt_gst_element_query_position(fileSink, format, &duration); + gst_object_unref(GST_OBJECT(fileSink)); + if (ret) + return duration / 1000000; +@@ -795,129 +774,57 @@ void CameraBinSession::setMetaData(const QMap &data) + { + m_metaData = data; + +- if (m_camerabin) { +- GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_camerabin), GST_TYPE_TAG_SETTER); +- GstElement *element = 0; +- while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { +- gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); +- +- QMapIterator it(data); +- while (it.hasNext()) { +- it.next(); +- const QString tagName = it.key(); +- const QVariant tagValue = it.value(); +- +- switch(tagValue.type()) { +- case QVariant::String: +- gst_tag_setter_add_tags(GST_TAG_SETTER(element), +- GST_TAG_MERGE_REPLACE, +- tagName.toUtf8().constData(), +- tagValue.toString().toUtf8().constData(), +- NULL); +- break; +- case QVariant::Int: +- case QVariant::LongLong: +- gst_tag_setter_add_tags(GST_TAG_SETTER(element), +- GST_TAG_MERGE_REPLACE, +- tagName.toUtf8().constData(), +- tagValue.toInt(), +- NULL); +- break; +- case QVariant::Double: +- gst_tag_setter_add_tags(GST_TAG_SETTER(element), +- GST_TAG_MERGE_REPLACE, +- tagName.toUtf8().constData(), +- tagValue.toDouble(), +- NULL); +- break; +- case QVariant::DateTime: { +- 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()), +- NULL); +- break; +- } +- default: +- break; +- } +- } +- } +- gst_iterator_free(elements); +- } ++ if (m_camerabin) ++ QGstUtils::setMetaData(m_camerabin, data); + } + + bool CameraBinSession::processSyncMessage(const QGstreamerMessage &message) + { + GstMessage* gm = message.rawMessage(); +- const GstStructure *st; +- const GValue *image; +- GstBuffer *buffer = NULL; + + if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) { +- if (m_captureMode == QCamera::CaptureStillImage && +- gst_structure_has_name(gm->structure, "preview-image")) { +- st = gst_message_get_structure(gm); +- +- if (gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER)) { +- image = gst_structure_get_value(st, "buffer"); +- if (image) { +- buffer = gst_value_get_buffer(image); +- +- QImage img; +- +- GstCaps *caps = gst_buffer_get_caps(buffer); +- if (caps) { +- GstStructure *structure = gst_caps_get_structure(caps, 0); +- gint width = 0; +- gint height = 0; +-#if CAMERABIN_DEBUG +- qDebug() << "Preview caps:" << gst_structure_to_string(structure); ++ const GstStructure *st = gst_message_get_structure(gm); ++ const GValue *sampleValue = 0; ++ if (m_captureMode == QCamera::CaptureStillImage ++ && gst_structure_has_name(st, "preview-image") ++#if GST_CHECK_VERSION(1,0,0) ++ && gst_structure_has_field_typed(st, "sample", GST_TYPE_SAMPLE) ++ && (sampleValue = gst_structure_get_value(st, "sample"))) { ++ GstSample * const sample = gst_value_get_sample(sampleValue); ++ GstCaps * const previewCaps = gst_sample_get_caps(sample); ++ GstBuffer * const buffer = gst_sample_get_buffer(sample); ++#else ++ && gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER) ++ && (sampleValue = gst_structure_get_value(st, "buffer"))) { ++ GstBuffer * const buffer = gst_value_get_buffer(sampleValue); + #endif + +- if (structure && +- gst_structure_get_int(structure, "width", &width) && +- gst_structure_get_int(structure, "height", &height) && +- width > 0 && height > 0) { +- if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { +- QImage::Format format = QImage::Format_Invalid; +- int bpp = 0; +- gst_structure_get_int(structure, "bpp", &bpp); +- +- if (bpp == 24) +- format = QImage::Format_RGB888; +- else if (bpp == 32) +- format = QImage::Format_RGB32; +- +- if (format != QImage::Format_Invalid) { +- img = QImage((const uchar *)buffer->data, width, height, format); +- img.bits(); //detach +- } +- } +- } +- gst_caps_unref(caps); +- +- static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageExposed); +- exposedSignal.invoke(this, +- Qt::QueuedConnection, +- Q_ARG(int,m_requestId)); +- +- static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageCaptured); +- capturedSignal.invoke(this, +- Qt::QueuedConnection, +- Q_ARG(int,m_requestId), +- Q_ARG(QImage,img)); +- } +- +- } +- return true; ++ QImage image; ++#if GST_CHECK_VERSION(1,0,0) ++ GstVideoInfo previewInfo; ++ if (gst_video_info_from_caps(&previewInfo, previewCaps)) ++ image = QGstUtils::bufferToImage(buffer, previewInfo); ++ gst_sample_unref(sample); ++#else ++ image = QGstUtils::bufferToImage(buffer); ++ gst_buffer_unref(buffer); ++#endif ++ if (!image.isNull()) { ++ static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageExposed); ++ exposedSignal.invoke(this, ++ Qt::QueuedConnection, ++ Q_ARG(int,m_requestId)); ++ ++ static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageCaptured); ++ capturedSignal.invoke(this, ++ Qt::QueuedConnection, ++ Q_ARG(int,m_requestId), ++ Q_ARG(QImage,image)); + } ++ return true; + } + #ifdef HAVE_GST_PHOTOGRAPHY +- if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) ++ if (gst_structure_has_name(st, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) + m_cameraFocusControl->handleFocusMessage(gm); + #endif + } +@@ -1109,20 +1016,12 @@ QList< QPair > CameraBinSession::supportedFrameRates(const QSize &frame + if (frameSize.isEmpty()) { + caps = gst_caps_copy(supportedCaps); + } else { +- GstCaps *filter = gst_caps_new_full( +- gst_structure_new( +- "video/x-raw-rgb", +- "width" , G_TYPE_INT , frameSize.width(), +- "height" , G_TYPE_INT, frameSize.height(), NULL), +- gst_structure_new( +- "video/x-raw-yuv", +- "width" , G_TYPE_INT, frameSize.width(), +- "height" , G_TYPE_INT, frameSize.height(), NULL), +- gst_structure_new( +- "image/jpeg", +- "width" , G_TYPE_INT, frameSize.width(), +- "height" , G_TYPE_INT, frameSize.height(), NULL), +- NULL); ++ GstCaps *filter = QGstUtils::videoFilterCaps(); ++ gst_caps_set_simple( ++ filter, ++ "width", G_TYPE_INT, frameSize.width(), ++ "height", G_TYPE_INT, frameSize.height(), ++ NULL); + + caps = gst_caps_intersect(supportedCaps, filter); + gst_caps_unref(filter); +@@ -1133,7 +1032,7 @@ QList< QPair > CameraBinSession::supportedFrameRates(const QSize &frame + caps = gst_caps_make_writable(caps); + for (uint i=0; i > CameraBinSession::supportedFrameRates(const QSize &frame + gst_structure_remove_all_fields(structure); + gst_structure_set_value(structure, "framerate", &rate); + } ++#if GST_CHECK_VERSION(1,0,0) ++ caps = gst_caps_simplify(caps); ++#else + gst_caps_do_simplify(caps); +- ++#endif + + for (uint i=0; i > CameraBinSession::supportedFrameRates(const QSize &frame + qSort(res.begin(), res.end(), rateLessThan); + + #if CAMERABIN_DEBUG +- qDebug() << "Supported rates:" << gst_caps_to_string(caps); ++ qDebug() << "Supported rates:" << caps; + qDebug() << res; + #endif + +@@ -1213,31 +1115,24 @@ QList CameraBinSession::supportedResolutions(QPair rate, + SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY : SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY, + &supportedCaps, NULL); + +- if (!supportedCaps) +- return res; +- + #if CAMERABIN_DEBUG +- qDebug() << "Source caps:" << gst_caps_to_string(supportedCaps); ++ qDebug() << "Source caps:" << supportedCaps; + #endif + ++ if (!supportedCaps) ++ return res; ++ + GstCaps *caps = 0; + bool isContinuous = false; + + if (rate.first <= 0 || rate.second <= 0) { + caps = gst_caps_copy(supportedCaps); + } else { +- GstCaps *filter = gst_caps_new_full( +- gst_structure_new( +- "video/x-raw-rgb", +- "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), +- gst_structure_new( +- "video/x-raw-yuv", +- "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), +- gst_structure_new( +- "image/jpeg", +- "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), +- NULL); +- ++ GstCaps *filter = QGstUtils::videoFilterCaps(); ++ gst_caps_set_simple( ++ filter, ++ "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, ++ NULL); + caps = gst_caps_intersect(supportedCaps, filter); + gst_caps_unref(filter); + } +@@ -1247,7 +1142,7 @@ QList CameraBinSession::supportedResolutions(QPair rate, + caps = gst_caps_make_writable(caps); + for (uint i=0; i CameraBinSession::supportedResolutions(QPair rate, + gst_structure_set_value(structure, "width", &w); + gst_structure_set_value(structure, "height", &h); + } ++ ++#if GST_CHECK_VERSION(1,0,0) ++ caps = gst_caps_simplify(caps); ++#else + gst_caps_do_simplify(caps); ++#endif ++ + + for (uint i=0; i + + #include + +@@ -175,7 +176,7 @@ GstElement *QGstreamerAudioEncode::createEncoder() + + if (m_audioSettings.sampleRate() > 0 || m_audioSettings.channelCount() > 0) { + GstCaps *caps = gst_caps_new_empty(); +- GstStructure *structure = gst_structure_new("audio/x-raw-int", NULL); ++ GstStructure *structure = qt_gst_structure_new_empty(QT_GSTREAMER_RAW_AUDIO_MIME); + + if (m_audioSettings.sampleRate() > 0) + gst_structure_set(structure, "rate", G_TYPE_INT, m_audioSettings.sampleRate(), NULL ); +diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp +index 97a165d..1ab98cd 100644 +--- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp ++++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp +@@ -62,27 +62,25 @@ + + QT_BEGIN_NAMESPACE + +-QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObject *parent): +- QMediaService(parent) +-{ +- m_captureSession = 0; +- m_cameraControl = 0; +- m_metaDataControl = 0; +- ++QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObject *parent) ++ : QMediaService(parent) ++ , m_captureSession(0) ++ , m_cameraControl(0) + #if defined(USE_GSTREAMER_CAMERA) +- m_videoInput = 0; ++ , m_videoInput(0) + #endif +- m_audioInputSelector = 0; +- m_videoInputDevice = 0; +- +- m_videoOutput = 0; +- m_videoRenderer = 0; +- m_videoWindow = 0; ++ , m_metaDataControl(0) ++ , m_audioInputSelector(0) ++ , m_videoInputDevice(0) ++ , m_videoOutput(0) ++ , m_videoRenderer(0) ++ , m_videoWindow(0) + #if defined(HAVE_WIDGETS) +- m_videoWidgetControl = 0; ++ , m_videoWidgetControl(0) + #endif +- m_imageCaptureControl = 0; +- ++ , m_imageCaptureControl(0) ++ , m_audioProbeControl(0) ++{ + if (service == Q_MEDIASERVICE_AUDIOSOURCE) { + m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this); + } +@@ -163,12 +161,12 @@ QMediaControl *QGstreamerCaptureService::requestControl(const char *name) + return m_imageCaptureControl; + + if (qstrcmp(name,QMediaAudioProbeControl_iid) == 0) { +- if (m_captureSession) { +- QGstreamerAudioProbeControl *probe = new QGstreamerAudioProbeControl(this); +- m_captureSession->addProbe(probe); +- return probe; ++ if (!m_audioProbeControl) { ++ m_audioProbeControl = new QGstreamerAudioProbeControl(this); ++ m_captureSession->addProbe(m_audioProbeControl); + } +- return 0; ++ m_audioProbeControl->ref.ref(); ++ return m_audioProbeControl; + } + + if (!m_videoOutput) { +@@ -194,17 +192,15 @@ QMediaControl *QGstreamerCaptureService::requestControl(const char *name) + + void QGstreamerCaptureService::releaseControl(QMediaControl *control) + { +- if (control && control == m_videoOutput) { ++ if (!control) { ++ return; ++ } else if (control == m_videoOutput) { + m_videoOutput = 0; + m_captureSession->setVideoPreview(0); +- } +- +- QGstreamerAudioProbeControl* audioProbe = qobject_cast(control); +- if (audioProbe) { +- if (m_captureSession) +- m_captureSession->removeProbe(audioProbe); +- delete audioProbe; +- return; ++ } else if (control == m_audioProbeControl && !m_audioProbeControl->ref.deref()) { ++ m_captureSession->removeProbe(m_audioProbeControl); ++ delete m_audioProbeControl; ++ m_audioProbeControl = 0; + } + } + +diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h +index 7ff8ce2..e0cf4ee 100644 +--- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h ++++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h +@@ -43,6 +43,7 @@ QT_BEGIN_NAMESPACE + class QAudioInputSelectorControl; + class QVideoDeviceSelectorControl; + ++class QGstreamerAudioProbeControl; + class QGstreamerCaptureSession; + class QGstreamerCameraControl; + class QGstreamerMessage; +@@ -86,6 +87,8 @@ private: + QMediaControl *m_videoWidgetControl; + #endif + QGstreamerImageCaptureControl *m_imageCaptureControl; ++ ++ QGstreamerAudioProbeControl *m_audioProbeControl; + }; + + QT_END_NAMESPACE +diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp +index 0ac34ee..85ed687 100644 +--- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp ++++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp +@@ -110,90 +110,16 @@ QMultimedia::SupportEstimate QGstreamerCaptureServicePlugin::hasSupport(const QS + return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet); + } + ++ ++static bool isEncoderOrMuxer(GstElementFactory *factory) ++{ ++ return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_MUXER) ++ || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_ENCODER); ++} ++ + void QGstreamerCaptureServicePlugin::updateSupportedMimeTypes() const + { +- //enumerate supported mime types +- gst_init(NULL, NULL); +- +- GList *plugins, *orig_plugins; +- orig_plugins = plugins = gst_default_registry_get_plugin_list (); +- +- while (plugins) { +- GList *features, *orig_features; +- +- GstPlugin *plugin = (GstPlugin *) (plugins->data); +- plugins = g_list_next (plugins); +- +- if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED +- continue; +- +- orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get_default (), +- plugin->desc.name); +- while (features) { +- if (!G_UNLIKELY(features->data == NULL)) { +- GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); +- if (GST_IS_ELEMENT_FACTORY (feature)) { +- GstElementFactory *factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); +- if (factory +- && factory->numpadtemplates > 0 +- && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0 +- || qstrcmp(factory->details.klass, "Codec/Decoder/Video") == 0 +- || qstrcmp(factory->details.klass, "Codec/Demux") == 0 )) { +- const GList *pads = factory->staticpadtemplates; +- while (pads) { +- GstStaticPadTemplate *padtemplate = (GstStaticPadTemplate*)(pads->data); +- pads = g_list_next (pads); +- if (padtemplate->direction != GST_PAD_SINK) +- continue; +- if (padtemplate->static_caps.string) { +- GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); +- if (!gst_caps_is_any (caps) && ! gst_caps_is_empty (caps)) { +- for (guint i = 0; i < gst_caps_get_size(caps); i++) { +- GstStructure *structure = gst_caps_get_structure(caps, i); +- QString nameLowcase = QString(gst_structure_get_name (structure)).toLower(); +- +- m_supportedMimeTypeSet.insert(nameLowcase); +- if (nameLowcase.contains("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(str); +- QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); +- foreach (const QString &e, elements) +- m_supportedMimeTypeSet.insert(nameLowcase + e); +- g_free (str); +- } +- } +- } +- } +- gst_caps_unref(caps); +- } +- } +- gst_object_unref (factory); +- } +- } else if (GST_IS_TYPE_FIND_FACTORY(feature)) { +- QString name(gst_plugin_feature_get_name(feature)); +- if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type +- m_supportedMimeTypeSet.insert(name.toLower()); +- } +- } +- features = g_list_next (features); +- } +- gst_plugin_feature_list_free (orig_features); +- } +- gst_plugin_list_free (orig_plugins); +- +-#if defined QT_SUPPORTEDMIMETYPES_DEBUG +- QStringList list = m_supportedMimeTypeSet.toList(); +- list.sort(); +- if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { +- foreach (const QString &type, list) +- qDebug() << type; +- } +-#endif ++ m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isEncoderOrMuxer); + } + + QStringList QGstreamerCaptureServicePlugin::supportedMimeTypes() const +diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp +index a2bd80d..af5b339 100644 +--- a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp ++++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp +@@ -45,6 +45,7 @@ + + #include + #include ++#include + + #include + #include +@@ -52,7 +53,6 @@ + #include + #include + #include +- + #include + + QT_BEGIN_NAMESPACE +@@ -64,7 +64,7 @@ QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::Cap + m_waitingForEos(false), + m_pipelineMode(EmptyPipeline), + m_captureMode(captureMode), +- m_audioBufferProbeId(-1), ++ m_audioProbe(0), + m_audioInputFactory(0), + m_audioPreviewFactory(0), + m_videoInputFactory(0), +@@ -169,7 +169,7 @@ GstElement *QGstreamerCaptureSession::buildEncodeBin() + + if (m_captureMode & Video) { + GstElement *videoQueue = gst_element_factory_make("queue", "video-encode-queue"); +- GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-encoder"); ++ GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-encoder"); + GstElement *videoscale = gst_element_factory_make("videoscale","videoscale-encoder"); + gst_bin_add_many(GST_BIN(encodeBin), videoQueue, colorspace, videoscale, NULL); + +@@ -280,7 +280,7 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() + + if (m_viewfinderInterface) { + GstElement *bin = gst_bin_new("video-preview-bin"); +- GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview"); ++ GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-preview"); + GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video-preview"); + GstElement *preview = m_viewfinderInterface->videoSink(); + +@@ -299,36 +299,25 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() + resolution = m_imageEncodeControl->imageSettings().resolution(); + } + +- if (!resolution.isEmpty() || frameRate > 0.001) { +- GstCaps *caps = gst_caps_new_empty(); +- QStringList structureTypes; +- structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb"; +- +- foreach(const QString &structureType, structureTypes) { +- GstStructure *structure = gst_structure_new(structureType.toLatin1().constData(), NULL); +- +- if (!resolution.isEmpty()) { +- gst_structure_set(structure, "width", G_TYPE_INT, resolution.width(), NULL); +- gst_structure_set(structure, "height", G_TYPE_INT, resolution.height(), NULL); +- } +- +- if (frameRate > 0.001) { +- QPair rate = m_videoEncodeControl->rateAsRational(); ++ GstCaps *caps = QGstUtils::videoFilterCaps(); + +- //qDebug() << "frame rate:" << num << denum; ++ if (!resolution.isEmpty()) { ++ gst_caps_set_simple(caps, "width", G_TYPE_INT, resolution.width(), NULL); ++ gst_caps_set_simple(caps, "height", G_TYPE_INT, resolution.height(), NULL); ++ } ++ if (frameRate > 0.001) { ++ QPair rate = m_videoEncodeControl->rateAsRational(); + +- gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); +- } ++ //qDebug() << "frame rate:" << num << denum; + +- gst_caps_append_structure(caps,structure); +- } ++ gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); ++ } + +- //qDebug() << "set video preview caps filter:" << gst_caps_to_string(caps); ++ //qDebug() << "set video preview caps filter:" << gst_caps_to_string(caps); + +- g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); ++ g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); + +- gst_caps_unref(caps); +- } ++ gst_caps_unref(caps); + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(colorspace, "sink"); +@@ -342,7 +331,7 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() + previewElement = gst_element_factory_make("fakesink", "video-preview"); + #else + GstElement *bin = gst_bin_new("video-preview-bin"); +- GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview"); ++ GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-preview"); + GstElement *preview = gst_element_factory_make("ximagesink", "video-preview"); + gst_bin_add_many(GST_BIN(bin), colorspace, preview, NULL); + gst_element_link(colorspace,preview); +@@ -360,101 +349,49 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() + return previewElement; + } + +- +-static gboolean passImageFilter(GstElement *element, +- GstBuffer *buffer, +- void *appdata) ++void QGstreamerCaptureSession::probeCaps(GstCaps *caps) + { +- Q_UNUSED(element); +- Q_UNUSED(buffer); +- +- QGstreamerCaptureSession *session = (QGstreamerCaptureSession *)appdata; +- if (session->m_passImage || session->m_passPrerollImage) { +- session->m_passImage = false; +- +- if (session->m_passPrerollImage) { +- session->m_passPrerollImage = false; +- return TRUE; +- } +- session->m_passPrerollImage = false; +- +- QImage img; +- +- GstCaps *caps = gst_buffer_get_caps(buffer); +- if (caps) { +- GstStructure *structure = gst_caps_get_structure (caps, 0); +- gint width = 0; +- gint height = 0; +- +- if (structure && +- gst_structure_get_int(structure, "width", &width) && +- gst_structure_get_int(structure, "height", &height) && +- width > 0 && height > 0) { +- if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { +- guint32 fourcc = 0; +- gst_structure_get_fourcc(structure, "format", &fourcc); +- +- if (fourcc == GST_MAKE_FOURCC('I','4','2','0')) { +- img = QImage(width/2, height/2, QImage::Format_RGB32); +- +- const uchar *data = (const uchar *)buffer->data; ++#if GST_CHECK_VERSION(1,0,0) ++ gst_video_info_from_caps(&m_previewInfo, caps); ++#else ++ Q_UNUSED(caps); ++#endif ++} + +- for (int y=0; ydata, +- width, +- height, +- format); +- img.bits(); //detach +- } +- } +- } +- gst_caps_unref(caps); +- } ++ if (img.isNull()) ++ return true; + +- static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageExposed); +- exposedSignal.invoke(session, +- Qt::QueuedConnection, +- Q_ARG(int,session->m_imageRequestId)); ++ static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageExposed); ++ exposedSignal.invoke(this, ++ Qt::QueuedConnection, ++ Q_ARG(int,m_imageRequestId)); + +- static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageCaptured); +- capturedSignal.invoke(session, +- Qt::QueuedConnection, +- Q_ARG(int,session->m_imageRequestId), +- Q_ARG(QImage,img)); ++ static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageCaptured); ++ capturedSignal.invoke(this, ++ Qt::QueuedConnection, ++ Q_ARG(int,m_imageRequestId), ++ Q_ARG(QImage,img)); + +- return TRUE; +- } else { +- return FALSE; +- } ++ return true; + } + + static gboolean saveImageFilter(GstElement *element, +@@ -471,7 +408,15 @@ static gboolean saveImageFilter(GstElement *element, + if (!fileName.isEmpty()) { + QFile f(fileName); + if (f.open(QFile::WriteOnly)) { +- f.write((const char *)buffer->data, buffer->size); ++#if GST_CHECK_VERSION(1,0,0) ++ GstMapInfo info; ++ if (gst_buffer_map(buffer, &info, GST_MAP_READ)) { ++ f.write(reinterpret_cast(info.data), info.size); ++ gst_buffer_unmap(buffer, &info); ++ } ++#else ++ f.write(reinterpret_cast(buffer->data), buffer->size); ++#endif + f.close(); + + static QMetaMethod savedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageSaved); +@@ -489,18 +434,19 @@ GstElement *QGstreamerCaptureSession::buildImageCapture() + { + GstElement *bin = gst_bin_new("image-capture-bin"); + GstElement *queue = gst_element_factory_make("queue", "queue-image-capture"); +- GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-image-capture"); ++ GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-image-capture"); + GstElement *encoder = gst_element_factory_make("jpegenc", "image-encoder"); + GstElement *sink = gst_element_factory_make("fakesink","sink-image-capture"); + + GstPad *pad = gst_element_get_static_pad(queue, "src"); + Q_ASSERT(pad); +- gst_pad_add_buffer_probe(pad, G_CALLBACK(passImageFilter), this); ++ ++ addProbeToPad(pad, false); ++ + gst_object_unref(GST_OBJECT(pad)); + + g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, NULL); +- g_signal_connect(G_OBJECT(sink), "handoff", +- G_CALLBACK(saveImageFilter), this); ++ g_signal_connect(G_OBJECT(sink), "handoff", G_CALLBACK(saveImageFilter), this); + + gst_bin_add_many(GST_BIN(bin), queue, colorspace, encoder, sink, NULL); + gst_element_link_many(queue, colorspace, encoder, sink, NULL); +@@ -715,6 +661,8 @@ void QGstreamerCaptureSession::dumpGraph(const QString &fileName) + _gst_debug_bin_to_dot_file(GST_BIN(m_pipeline), + GstDebugGraphDetails(/*GST_DEBUG_GRAPH_SHOW_ALL |*/ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES), + fileName.toLatin1()); ++#else ++ Q_UNUSED(fileName); + #endif + } + +@@ -877,10 +825,8 @@ void QGstreamerCaptureSession::setState(QGstreamerCaptureSession::State newState + + qint64 QGstreamerCaptureSession::duration() const + { +- GstFormat format = GST_FORMAT_TIME; +- gint64 duration = 0; +- +- if ( m_encodeBin && gst_element_query_position(m_encodeBin, &format, &duration)) ++ gint64 duration = 0; ++ if (m_encodeBin && qt_gst_element_query_position(m_encodeBin, GST_FORMAT_TIME, &duration)) + return duration / 1000000; + else + return 0; +@@ -896,50 +842,8 @@ void QGstreamerCaptureSession::setMetaData(const QMap &dat + //qDebug() << "QGstreamerCaptureSession::setMetaData" << data; + m_metaData = data; + +- if (m_encodeBin) { +- GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_encodeBin), GST_TYPE_TAG_SETTER); +- GstElement *element = 0; +- while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { +- //qDebug() << "found element with tag setter interface:" << gst_element_get_name(element); +- QMapIterator it(data); +- while (it.hasNext()) { +- it.next(); +- const QString tagName = it.key(); +- const QVariant tagValue = it.value(); +- +- +- switch(tagValue.type()) { +- case QVariant::String: +- gst_tag_setter_add_tags(GST_TAG_SETTER(element), +- GST_TAG_MERGE_REPLACE_ALL, +- tagName.toUtf8().constData(), +- tagValue.toString().toUtf8().constData(), +- NULL); +- break; +- case QVariant::Int: +- case QVariant::LongLong: +- gst_tag_setter_add_tags(GST_TAG_SETTER(element), +- GST_TAG_MERGE_REPLACE_ALL, +- tagName.toUtf8().constData(), +- tagValue.toInt(), +- NULL); +- break; +- case QVariant::Double: +- gst_tag_setter_add_tags(GST_TAG_SETTER(element), +- GST_TAG_MERGE_REPLACE_ALL, +- tagName.toUtf8().constData(), +- tagValue.toDouble(), +- NULL); +- break; +- default: +- break; +- } +- +- } +- +- } +- gst_iterator_free(elements); +- } ++ if (m_encodeBin) ++ QGstUtils::setMetaData(GST_BIN(m_encodeBin), data); + } + + bool QGstreamerCaptureSession::processBusMessage(const QGstreamerMessage &message) +@@ -1058,34 +962,16 @@ void QGstreamerCaptureSession::setVolume(qreal volume) + + void QGstreamerCaptureSession::addProbe(QGstreamerAudioProbeControl* probe) + { +- QMutexLocker locker(&m_audioProbeMutex); +- +- if (m_audioProbes.contains(probe)) +- return; +- +- m_audioProbes.append(probe); ++ Q_ASSERT(!m_audioProbe); ++ m_audioProbe = probe; ++ addAudioBufferProbe(); + } + + void QGstreamerCaptureSession::removeProbe(QGstreamerAudioProbeControl* probe) + { +- QMutexLocker locker(&m_audioProbeMutex); +- m_audioProbes.removeOne(probe); +-} +- +-gboolean QGstreamerCaptureSession::padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) +-{ +- Q_UNUSED(pad); +- +- QGstreamerCaptureSession *session = reinterpret_cast(user_data); +- QMutexLocker locker(&session->m_audioProbeMutex); +- +- if (session->m_audioProbes.isEmpty()) +- return TRUE; +- +- foreach (QGstreamerAudioProbeControl* probe, session->m_audioProbes) +- probe->bufferProbed(buffer); +- +- return TRUE; ++ Q_ASSERT(m_audioProbe == probe); ++ removeAudioBufferProbe(); ++ m_audioProbe = 0; + } + + GstPad *QGstreamerCaptureSession::getAudioProbePad() +@@ -1114,26 +1000,25 @@ GstPad *QGstreamerCaptureSession::getAudioProbePad() + + void QGstreamerCaptureSession::removeAudioBufferProbe() + { +- if (m_audioBufferProbeId == -1) ++ if (!m_audioProbe) + return; + + GstPad *pad = getAudioProbePad(); + if (pad) { +- gst_pad_remove_buffer_probe(pad, m_audioBufferProbeId); +- gst_object_unref(G_OBJECT(pad)); ++ m_audioProbe->removeProbeFromPad(pad); ++ gst_object_unref(GST_OBJECT(pad)); + } +- +- m_audioBufferProbeId = -1; + } + + void QGstreamerCaptureSession::addAudioBufferProbe() + { +- Q_ASSERT(m_audioBufferProbeId == -1); ++ if (!m_audioProbe) ++ return; + + GstPad *pad = getAudioProbePad(); + if (pad) { +- m_audioBufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padAudioBufferProbe), this); +- gst_object_unref(G_OBJECT(pad)); ++ m_audioProbe->addProbeToPad(pad); ++ gst_object_unref(GST_OBJECT(pad)); + } + } + +diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h +index a759f22..ad26327 100644 +--- a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h ++++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h +@@ -41,8 +41,10 @@ + #include + + #include ++#include + + #include ++#include + + QT_BEGIN_NAMESPACE + +@@ -70,7 +72,10 @@ public: + virtual QList supportedResolutions(qreal frameRate = -1) const = 0; + }; + +-class QGstreamerCaptureSession : public QObject, public QGstreamerBusMessageFilter ++class QGstreamerCaptureSession ++ : public QObject ++ , public QGstreamerBusMessageFilter ++ , private QGstreamerBufferProbe + { + Q_OBJECT + Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) +@@ -131,7 +136,6 @@ public: + + void addProbe(QGstreamerAudioProbeControl* probe); + void removeProbe(QGstreamerAudioProbeControl* probe); +- static gboolean padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); + + signals: + void stateChanged(QGstreamerCaptureSession::State state); +@@ -156,6 +160,9 @@ public slots: + void setVolume(qreal volume); + + private: ++ void probeCaps(GstCaps *caps); ++ bool probeBuffer(GstBuffer *buffer); ++ + enum PipelineMode { EmptyPipeline, PreviewPipeline, RecordingPipeline, PreviewAndRecordingPipeline }; + + GstElement *buildEncodeBin(); +@@ -180,9 +187,7 @@ private: + QGstreamerCaptureSession::CaptureMode m_captureMode; + QMap m_metaData; + +- QList m_audioProbes; +- QMutex m_audioProbeMutex; +- int m_audioBufferProbeId; ++ QGstreamerAudioProbeControl *m_audioProbe; + + QGstreamerElementFactory *m_audioInputFactory; + QGstreamerElementFactory *m_audioPreviewFactory; +@@ -217,6 +222,10 @@ private: + + GstElement *m_encodeBin; + ++#if GST_CHECK_VERSION(1,0,0) ++ GstVideoInfo m_previewInfo; ++#endif ++ + public: + bool m_passImage; + bool m_passPrerollImage; +diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp +index 2f0d0ee..81b85d7 100644 +--- a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp ++++ b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp +@@ -34,7 +34,7 @@ + #include "qgstreamervideoencode.h" + #include "qgstreamercapturesession.h" + #include "qgstreamermediacontainercontrol.h" +- ++#include + #include + + #include +@@ -147,7 +147,7 @@ GstElement *QGstreamerVideoEncode::createEncoder() + GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video"); + gst_bin_add(encoderBin, capsFilter); + +- GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", NULL); ++ GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, NULL); + gst_bin_add(encoderBin, colorspace); + gst_bin_add(encoderBin, encoderElement); + +@@ -252,27 +252,22 @@ GstElement *QGstreamerVideoEncode::createEncoder() + } + + if (!m_videoSettings.resolution().isEmpty() || m_videoSettings.frameRate() > 0.001) { +- GstCaps *caps = gst_caps_new_empty(); +- QStringList structureTypes; +- structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb"; +- +- foreach(const QString &structureType, structureTypes) { +- GstStructure *structure = gst_structure_new(structureType.toLatin1().constData(), NULL); +- +- if (!m_videoSettings.resolution().isEmpty()) { +- gst_structure_set(structure, "width", G_TYPE_INT, m_videoSettings.resolution().width(), NULL); +- gst_structure_set(structure, "height", G_TYPE_INT, m_videoSettings.resolution().height(), NULL); +- } +- +- if (m_videoSettings.frameRate() > 0.001) { +- QPair rate = rateAsRational(); +- +- //qDebug() << "frame rate:" << num << denum; +- +- gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); +- } ++ GstCaps *caps = QGstUtils::videoFilterCaps(); ++ ++ if (!m_videoSettings.resolution().isEmpty()) { ++ gst_caps_set_simple( ++ caps, ++ "width", G_TYPE_INT, m_videoSettings.resolution().width(), ++ "height", G_TYPE_INT, m_videoSettings.resolution().height(), ++ NULL); ++ } + +- gst_caps_append_structure(caps,structure); ++ if (m_videoSettings.frameRate() > 0.001) { ++ QPair rate = rateAsRational(); ++ gst_caps_set_simple( ++ caps, ++ "framerate", GST_TYPE_FRACTION, rate.first, rate.second, ++ NULL); + } + + //qDebug() << "set video caps filter:" << gst_caps_to_string(caps); +diff --git a/src/plugins/gstreamer/mediaplayer/mediaplayer.pro b/src/plugins/gstreamer/mediaplayer/mediaplayer.pro +index 2ca9377..b986fc7 100644 +--- a/src/plugins/gstreamer/mediaplayer/mediaplayer.pro ++++ b/src/plugins/gstreamer/mediaplayer/mediaplayer.pro +@@ -28,4 +28,3 @@ SOURCES += \ + + OTHER_FILES += \ + mediaplayer.json +- +diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp +index fed756a..c1fb64a 100644 +--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp ++++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp +@@ -425,7 +425,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice * + m_session->loadFromUri(request); + #endif + +- + #if defined(HAVE_GST_APPSRC) + if (!request.url().isEmpty() || userStreamValid) { + #else +diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp +index ce267d7..84805b6 100644 +--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp ++++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp +@@ -51,7 +51,11 @@ + #include + + #if defined(Q_WS_MAEMO_6) && defined(__arm__) +-#include "qgstreamergltexturerenderer.h" ++#include "private/qgstreamergltexturerenderer.h" ++#endif ++ ++#if defined(HAVE_MIR) && defined (__arm__) ++#include "private/qgstreamermirtexturerenderer_p.h" + #endif + + #include "qgstreamerstreamscontrol.h" +@@ -66,6 +70,8 @@ QT_BEGIN_NAMESPACE + + QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): + QMediaService(parent) ++ , m_audioProbeControl(0) ++ , m_videoProbeControl(0) + , m_videoOutput(0) + , m_videoRenderer(0) + , m_videoWindow(0) +@@ -82,6 +88,8 @@ QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): + + #if defined(Q_WS_MAEMO_6) && defined(__arm__) + m_videoRenderer = new QGstreamerGLTextureRenderer(this); ++#elif defined(HAVE_MIR) && defined (__arm__) ++ m_videoRenderer = new QGstreamerMirTextureRenderer(this, m_session); + #else + m_videoRenderer = new QGstreamerVideoRenderer(this); + #endif +@@ -115,23 +123,23 @@ QMediaControl *QGstreamerPlayerService::requestControl(const char *name) + if (qstrcmp(name, QMediaAvailabilityControl_iid) == 0) + return m_availabilityControl; + +- if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) { +- if (m_session) { +- QGstreamerVideoProbeControl *probe = new QGstreamerVideoProbeControl(this); ++ if (qstrcmp(name, QMediaVideoProbeControl_iid) == 0) { ++ if (!m_videoProbeControl) { + increaseVideoRef(); +- m_session->addProbe(probe); +- return probe; ++ m_videoProbeControl = new QGstreamerVideoProbeControl(this); ++ m_session->addProbe(m_videoProbeControl); + } +- return 0; ++ m_videoProbeControl->ref.ref(); ++ return m_videoProbeControl; + } + +- if (qstrcmp(name,QMediaAudioProbeControl_iid) == 0) { +- if (m_session) { +- QGstreamerAudioProbeControl *probe = new QGstreamerAudioProbeControl(this); +- m_session->addProbe(probe); +- return probe; ++ if (qstrcmp(name, QMediaAudioProbeControl_iid) == 0) { ++ if (!m_audioProbeControl) { ++ m_audioProbeControl = new QGstreamerAudioProbeControl(this); ++ m_session->addProbe(m_audioProbeControl); + } +- return 0; ++ m_audioProbeControl->ref.ref(); ++ return m_audioProbeControl; + } + + if (!m_videoOutput) { +@@ -156,28 +164,21 @@ QMediaControl *QGstreamerPlayerService::requestControl(const char *name) + + void QGstreamerPlayerService::releaseControl(QMediaControl *control) + { +- if (control == m_videoOutput) { ++ if (!control) { ++ return; ++ } else if (control == m_videoOutput) { + m_videoOutput = 0; + m_control->setVideoOutput(0); + decreaseVideoRef(); +- } +- +- QGstreamerVideoProbeControl* videoProbe = qobject_cast(control); +- if (videoProbe) { +- if (m_session) { +- m_session->removeProbe(videoProbe); +- decreaseVideoRef(); +- } +- delete videoProbe; +- return; +- } +- +- QGstreamerAudioProbeControl* audioProbe = qobject_cast(control); +- if (audioProbe) { +- if (m_session) +- m_session->removeProbe(audioProbe); +- delete audioProbe; +- return; ++ } else if (control == m_videoProbeControl && !m_videoProbeControl->ref.deref()) { ++ m_session->removeProbe(m_videoProbeControl); ++ delete m_videoProbeControl; ++ m_videoProbeControl = 0; ++ decreaseVideoRef(); ++ } else if (control == m_audioProbeControl && !m_audioProbeControl->ref.deref()) { ++ m_session->removeProbe(m_audioProbeControl); ++ delete m_audioProbeControl; ++ m_audioProbeControl = 0; + } + } + +diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h +index f3081e9..22be262 100644 +--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h ++++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h +@@ -52,6 +52,8 @@ class QGstreamerStreamsControl; + class QGstreamerVideoRenderer; + class QGstreamerVideoWidgetControl; + class QGStreamerAvailabilityControl; ++class QGstreamerAudioProbeControl; ++class QGstreamerVideoProbeControl; + + class QGstreamerPlayerService : public QMediaService + { +@@ -70,6 +72,9 @@ private: + QGstreamerStreamsControl *m_streamsControl; + QGStreamerAvailabilityControl *m_availabilityControl; + ++ QGstreamerAudioProbeControl *m_audioProbeControl; ++ QGstreamerVideoProbeControl *m_videoProbeControl; ++ + QMediaControl *m_videoOutput; + QMediaControl *m_videoRenderer; + QMediaControl *m_videoWindow; +diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp +index 7d20b6d..f1fd421 100644 +--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp ++++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp +@@ -81,89 +81,15 @@ QMultimedia::SupportEstimate QGstreamerPlayerServicePlugin::hasSupport(const QSt + return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet); + } + +-void QGstreamerPlayerServicePlugin::updateSupportedMimeTypes() const ++static bool isDecoderOrDemuxer(GstElementFactory *factory) + { +- //enumerate supported mime types +- gst_init(NULL, NULL); +- +- GList *plugins, *orig_plugins; +- orig_plugins = plugins = gst_default_registry_get_plugin_list (); +- +- while (plugins) { +- GList *features, *orig_features; +- +- GstPlugin *plugin = (GstPlugin *) (plugins->data); +- plugins = g_list_next (plugins); +- +- if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED +- continue; +- +- orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get_default (), +- plugin->desc.name); +- while (features) { +- if (!G_UNLIKELY(features->data == NULL)) { +- GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); +- if (GST_IS_ELEMENT_FACTORY (feature)) { +- GstElementFactory *factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); +- if (factory +- && factory->numpadtemplates > 0 +- && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0 +- || qstrcmp(factory->details.klass, "Codec/Decoder/Video") == 0 +- || qstrcmp(factory->details.klass, "Codec/Demux") == 0 )) { +- const GList *pads = factory->staticpadtemplates; +- while (pads) { +- GstStaticPadTemplate *padtemplate = (GstStaticPadTemplate*)(pads->data); +- pads = g_list_next (pads); +- if (padtemplate->direction != GST_PAD_SINK) +- continue; +- if (padtemplate->static_caps.string) { +- GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); +- if (!gst_caps_is_any (caps) && ! gst_caps_is_empty (caps)) { +- for (guint i = 0; i < gst_caps_get_size(caps); i++) { +- GstStructure *structure = gst_caps_get_structure(caps, i); +- QString nameLowcase = QString(gst_structure_get_name (structure)).toLower(); +- +- m_supportedMimeTypeSet.insert(nameLowcase); +- if (nameLowcase.contains("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(str); +- QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); +- foreach (const QString &e, elements) +- m_supportedMimeTypeSet.insert(nameLowcase + e); +- g_free (str); +- } +- } +- } +- } +- } +- } +- gst_object_unref (factory); +- } +- } else if (GST_IS_TYPE_FIND_FACTORY(feature)) { +- QString name(gst_plugin_feature_get_name(feature)); +- if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type +- m_supportedMimeTypeSet.insert(name.toLower()); +- } +- } +- features = g_list_next (features); +- } +- gst_plugin_feature_list_free (orig_features); +- } +- gst_plugin_list_free (orig_plugins); ++ return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DEMUXER) ++ || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DECODER); ++} + +-#if defined QT_SUPPORTEDMIMETYPES_DEBUG +- QStringList list = m_supportedMimeTypeSet.toList(); +- list.sort(); +- if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { +- foreach (const QString &type, list) +- qDebug() << type; +- } +-#endif ++void QGstreamerPlayerServicePlugin::updateSupportedMimeTypes() const ++{ ++ m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isDecoderOrDemuxer); + } + + QStringList QGstreamerPlayerServicePlugin::supportedMimeTypes() const +diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp +index 15924a6..b5c354d 100644 +--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp ++++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp +@@ -37,7 +37,9 @@ + #include + #include + #include ++#if !GST_CHECK_VERSION(1,0,0) + #include ++#endif + #include + #include + #include +@@ -85,6 +87,7 @@ typedef enum { + GST_PLAY_FLAG_BUFFERING = 0x000000100 + } GstPlayFlags; + ++#if !GST_CHECK_VERSION(1,0,0) + #define DEFAULT_RAW_CAPS \ + "video/x-raw-yuv; " \ + "video/x-raw-rgb; " \ +@@ -97,7 +100,9 @@ typedef enum { + "text/x-pango-markup; " \ + "video/x-dvd-subpicture; " \ + "subpicture/x-pgs" ++ + static GstStaticCaps static_RawCaps = GST_STATIC_CAPS(DEFAULT_RAW_CAPS); ++#endif + + QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + :QObject(parent), +@@ -105,7 +110,9 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + m_pendingState(QMediaPlayer::StoppedState), + m_busHelper(0), + m_playbin(0), ++#if !GST_CHECK_VERSION(1,0,0) + m_usingColorspaceElement(false), ++#endif + m_videoSink(0), + m_pendingVideoSink(0), + m_nullVideoSink(0), +@@ -117,8 +124,8 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + #if defined(HAVE_GST_APPSRC) + m_appSrc(0), + #endif +- m_videoBufferProbeId(-1), +- m_audioBufferProbeId(-1), ++ m_videoProbe(0), ++ m_audioProbe(0), + m_volume(100), + m_playbackRate(1.0), + m_muted(false), +@@ -138,8 +145,7 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + Q_ASSERT(result == TRUE); + Q_UNUSED(result); + +- m_playbin = gst_element_factory_make("playbin2", NULL); +- ++ m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, NULL); + 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. +@@ -147,13 +153,14 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + int flags = GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | + GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_NATIVE_AUDIO; + #else +- int flags = 0; +- g_object_get(G_OBJECT(m_playbin), "flags", &flags, NULL); ++ int flags = GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO; + QByteArray envFlags = qgetenv("QT_GSTREAMER_PLAYBIN_FLAGS"); + if (!envFlags.isEmpty()) { + flags |= envFlags.toInt(); ++#if !GST_CHECK_VERSION(1,0,0) + } else { + flags |= GST_PLAY_FLAG_NATIVE_VIDEO; ++#endif + } + #endif + g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL); +@@ -185,12 +192,16 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + } + } + ++#if GST_CHECK_VERSION(1,0,0) ++ m_videoIdentity = gst_element_factory_make("identity", NULL); // floating ref ++#else + m_videoIdentity = GST_ELEMENT(g_object_new(gst_video_connector_get_type(), 0)); // floating ref + g_signal_connect(G_OBJECT(m_videoIdentity), "connection-failed", G_CALLBACK(insertColorSpaceElement), (gpointer)this); ++ m_colorSpace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "ffmpegcolorspace-vo"); + +- m_colorSpace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-vo"); + // might not get a parent, take ownership to avoid leak + qt_gst_object_ref_sink(GST_OBJECT(m_colorSpace)); ++#endif + + m_nullVideoSink = gst_element_factory_make("fakesink", NULL); + g_object_set(G_OBJECT(m_nullVideoSink), "sync", true, NULL); +@@ -206,7 +217,7 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(m_videoIdentity,"sink"); +- gst_element_add_pad(GST_ELEMENT(m_videoOutputBin), gst_ghost_pad_new("videosink", pad)); ++ gst_element_add_pad(GST_ELEMENT(m_videoOutputBin), gst_ghost_pad_new("sink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + if (m_playbin != 0) { +@@ -244,7 +255,9 @@ QGstreamerPlayerSession::~QGstreamerPlayerSession() + delete m_busHelper; + gst_object_unref(GST_OBJECT(m_bus)); + gst_object_unref(GST_OBJECT(m_playbin)); ++#if !GST_CHECK_VERSION(1,0,0) + gst_object_unref(GST_OBJECT(m_colorSpace)); ++#endif + gst_object_unref(GST_OBJECT(m_nullVideoSink)); + gst_object_unref(GST_OBJECT(m_videoOutputBin)); + } +@@ -339,12 +352,10 @@ qint64 QGstreamerPlayerSession::duration() const + + qint64 QGstreamerPlayerSession::position() const + { +- GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + +- if ( m_playbin && gst_element_query_position(m_playbin, &format, &position)) ++ if (m_playbin && qt_gst_element_query_position(m_playbin, GST_FORMAT_TIME, &position)) + m_lastPosition = position / 1000000; +- + return m_lastPosition; + } + +@@ -474,17 +485,26 @@ bool QGstreamerPlayerSession::isAudioAvailable() const + return m_audioAvailable; + } + ++#if GST_CHECK_VERSION(1,0,0) ++static GstPadProbeReturn block_pad_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) ++#else + static void block_pad_cb(GstPad *pad, gboolean blocked, gpointer user_data) ++#endif + { + Q_UNUSED(pad); ++#if GST_CHECK_VERSION(1,0,0) ++ Q_UNUSED(info); ++ Q_UNUSED(user_data); ++ return GST_PAD_PROBE_OK; ++#else + #ifdef DEBUG_PLAYBIN + qDebug() << "block_pad_cb, blocked:" << blocked; + #endif +- + if (blocked && user_data) { + QGstreamerPlayerSession *session = reinterpret_cast(user_data); + QMetaObject::invokeMethod(session, "finishVideoOutputChange", Qt::QueuedConnection); + } ++#endif + } + + void QGstreamerPlayerSession::updateVideoRenderer() +@@ -529,7 +549,7 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) + m_renderer = renderer; + + #ifdef DEBUG_VO_BIN_DUMP +- _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), ++ gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), + GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/), + "playbin_set"); + #endif +@@ -570,12 +590,14 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) + gst_element_set_state(m_videoSink, GST_STATE_NULL); + gst_element_set_state(m_playbin, GST_STATE_NULL); + ++#if !GST_CHECK_VERSION(1,0,0) + if (m_usingColorspaceElement) { + gst_element_unlink(m_colorSpace, m_videoSink); + gst_bin_remove(GST_BIN(m_videoOutputBin), m_colorSpace); + } else { + gst_element_unlink(m_videoIdentity, m_videoSink); + } ++#endif + + removeVideoBufferProbe(); + +@@ -585,8 +607,9 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) + + gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); + +- m_usingColorspaceElement = false; + bool linked = gst_element_link(m_videoIdentity, m_videoSink); ++#if !GST_CHECK_VERSION(1,0,0) ++ m_usingColorspaceElement = false; + if (!linked) { + m_usingColorspaceElement = true; + #ifdef DEBUG_PLAYBIN +@@ -595,6 +618,10 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) + gst_bin_add(GST_BIN(m_videoOutputBin), m_colorSpace); + linked = gst_element_link_many(m_videoIdentity, m_colorSpace, m_videoSink, NULL); + } ++#endif ++ ++ 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; +@@ -633,7 +660,11 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) + + //block pads, async to avoid locking in paused state + GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); ++#if GST_CHECK_VERSION(1,0,0) ++ this->pad_probe_id = gst_pad_add_probe(srcPad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BLOCKING), block_pad_cb, this, NULL); ++#else + gst_pad_set_blocked_async(srcPad, true, &block_pad_cb, this); ++#endif + gst_object_unref(GST_OBJECT(srcPad)); + + //Unpause the sink to avoid waiting until the buffer is processed +@@ -671,16 +702,22 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + } + + 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)) ++#if GST_CHECK_VERSION(1,0,0) ++ gst_pad_remove_probe(srcPad, this->pad_probe_id); ++#else + gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); ++#endif + + m_pendingVideoSink = 0; + gst_object_unref(GST_OBJECT(srcPad)); + return; + } + ++#if !GST_CHECK_VERSION(1,0,0) + if (m_usingColorspaceElement) { + gst_element_set_state(m_colorSpace, GST_STATE_NULL); + gst_element_set_state(m_videoSink, GST_STATE_NULL); +@@ -688,6 +725,9 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + gst_element_unlink(m_colorSpace, m_videoSink); + gst_bin_remove(GST_BIN(m_videoOutputBin), m_colorSpace); + } else { ++#else ++ { ++#endif + gst_element_set_state(m_videoSink, GST_STATE_NULL); + gst_element_unlink(m_videoIdentity, m_videoSink); + } +@@ -703,8 +743,9 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + + addVideoBufferProbe(); + +- m_usingColorspaceElement = false; + bool linked = gst_element_link(m_videoIdentity, m_videoSink); ++#if !GST_CHECK_VERSION(1,0,0) ++ m_usingColorspaceElement = false; + if (!linked) { + m_usingColorspaceElement = true; + #ifdef DEBUG_PLAYBIN +@@ -713,6 +754,7 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + gst_bin_add(GST_BIN(m_videoOutputBin), m_colorSpace); + linked = gst_element_link_many(m_videoIdentity, m_colorSpace, m_videoSink, NULL); + } ++#endif + + if (!linked) + qWarning() << "Linking video output element failed"; +@@ -720,6 +762,8 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + #ifdef DEBUG_PLAYBIN + qDebug() << "notify the video connector it has to emit a new segment message..."; + #endif ++ ++#if !GST_CHECK_VERSION(1,0,0) + //it's necessary to send a new segment event just before + //the first buffer pushed to the new sink + g_signal_emit_by_name(m_videoIdentity, +@@ -727,7 +771,7 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + true //emit connection-failed signal + //to have a chance to insert colorspace element + ); +- ++#endif + + GstState state = GST_STATE_VOID_PENDING; + +@@ -743,8 +787,10 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + break; + } + ++#if !GST_CHECK_VERSION(1,0,0) + if (m_usingColorspaceElement) + gst_element_set_state(m_colorSpace, state); ++#endif + + gst_element_set_state(m_videoSink, state); + +@@ -760,16 +806,23 @@ void QGstreamerPlayerSession::finishVideoOutputChange() + + //don't have to wait here, it will unblock eventually + if (gst_pad_is_blocked(srcPad)) +- gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); ++#if GST_CHECK_VERSION(1,0,0) ++ gst_pad_remove_probe(srcPad, this->pad_probe_id); ++#else ++ gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); ++#endif ++ + gst_object_unref(GST_OBJECT(srcPad)); + + #ifdef DEBUG_VO_BIN_DUMP +- _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), +- GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/), ++ gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), ++ GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* | GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES */), + "playbin_finish"); + #endif + } + ++#if !GST_CHECK_VERSION(1,0,0) ++ + void QGstreamerPlayerSession::insertColorSpaceElement(GstElement *element, gpointer data) + { + #ifdef DEBUG_PLAYBIN +@@ -814,6 +867,7 @@ void QGstreamerPlayerSession::insertColorSpaceElement(GstElement *element, gpoin + gst_element_set_state(session->m_colorSpace, state); + } + ++#endif + + bool QGstreamerPlayerSession::isVideoAvailable() const + { +@@ -830,6 +884,7 @@ bool QGstreamerPlayerSession::play() + #ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; + #endif ++ + m_everPlayed = false; + if (m_playbin) { + m_pendingState = QMediaPlayer::PlayingState; +@@ -1161,21 +1216,20 @@ bool QGstreamerPlayerSession::processBusMessage(const QGstreamerMessage &message + case GST_MESSAGE_SEGMENT_DONE: + break; + case GST_MESSAGE_LATENCY: +-#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 13) ++#if GST_CHECK_VERSION(0,10,13) + case GST_MESSAGE_ASYNC_START: + break; + case GST_MESSAGE_ASYNC_DONE: + { +- GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; +- if (gst_element_query_position(m_playbin, &format, &position)) { ++ if (qt_gst_element_query_position(m_playbin, GST_FORMAT_TIME, &position)) { + position /= 1000000; + m_lastPosition = position; + emit positionChanged(position); + } + break; + } +-#if GST_VERSION_MICRO >= 23 ++#if GST_CHECK_VERSION(0,10,23) + case GST_MESSAGE_REQUEST_STATE: + #endif + #endif +@@ -1327,8 +1381,11 @@ void QGstreamerPlayerSession::getStreamsInfo() + default: + break; + } +- ++#if GST_CHECK_VERSION(1,0,0) ++ if (tags && GST_IS_TAG_LIST(tags)) { ++#else + if (tags && gst_is_tag_list(tags)) { ++#endif + gchar *languageCode = 0; + if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) + streamProperties[QMediaMetaData::Language] = QString::fromUtf8(languageCode); +@@ -1365,9 +1422,8 @@ void QGstreamerPlayerSession::updateVideoResolutionTag() + #endif + QSize size; + QSize aspectRatio; +- + GstPad *pad = gst_element_get_static_pad(m_videoIdentity, "src"); +- GstCaps *caps = gst_pad_get_negotiated_caps(pad); ++ GstCaps *caps = qt_gst_pad_get_current_caps(pad); + + if (caps) { + const GstStructure *structure = gst_caps_get_structure(caps, 0); +@@ -1407,11 +1463,10 @@ void QGstreamerPlayerSession::updateVideoResolutionTag() + + void QGstreamerPlayerSession::updateDuration() + { +- GstFormat format = GST_FORMAT_TIME; + gint64 gstDuration = 0; + int duration = -1; + +- if (m_playbin && gst_element_query_duration(m_playbin, &format, &gstDuration)) ++ if (m_playbin && qt_gst_element_query_duration(m_playbin, GST_FORMAT_TIME, &gstDuration)) + duration = gstDuration / 1000000; + + if (m_duration != duration) { +@@ -1467,7 +1522,7 @@ void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpo + + // The rest + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "extra-headers") != 0) { +- GstStructure *extras = gst_structure_empty_new("extras"); ++ GstStructure *extras = qt_gst_structure_new_empty("extras"); + + foreach (const QByteArray &rawHeader, self->m_request.rawHeaderList()) { + if (rawHeader == userAgentString) // Filter User-Agent +@@ -1528,7 +1583,8 @@ void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpo + qDebug() << "Current source is a non-live source"; + #endif + +- g_object_set(G_OBJECT(self->m_videoSink), "sync", !self->m_isLiveSource, NULL); ++ if (self->m_videoSink) ++ g_object_set(G_OBJECT(self->m_videoSink), "sync", !self->m_isLiveSource, NULL); + + gst_object_unref(source); + } +@@ -1623,7 +1679,11 @@ GstAutoplugSelectResult QGstreamerPlayerSession::handleAutoplugSelect(GstBin *bi + 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"); ++#if GST_CHECK_VERSION(1,0,0) ++ GstCaps *sinkCaps = gst_pad_query_caps(sinkPad, NULL); ++#else + GstCaps *sinkCaps = gst_pad_get_caps(sinkPad); ++#endif + + #if (GST_VERSION_MAJOR == 0) && ((GST_VERSION_MINOR < 10) || (GST_VERSION_MICRO < 33)) + if (!factory_can_src_any_caps(factory, sinkCaps)) +@@ -1652,8 +1712,10 @@ void QGstreamerPlayerSession::handleElementAdded(GstBin *bin, GstElement *elemen + // Disable on-disk buffering. + g_object_set(G_OBJECT(element), "temp-template", NULL, NULL); + } else if (g_str_has_prefix(elementName, "uridecodebin") || +- g_str_has_prefix(elementName, "decodebin2")) { +- ++#if GST_CHECK_VERSION(1,0,0) ++ g_str_has_prefix(elementName, "decodebin")) { ++#else ++ g_str_has_prefix(elementName, "decodebin2")) { + if (g_str_has_prefix(elementName, "uridecodebin")) { + // Add video/x-surface (VAAPI) to default raw formats + g_object_set(G_OBJECT(element), "caps", gst_static_caps_get(&static_RawCaps), NULL); +@@ -1661,7 +1723,7 @@ void QGstreamerPlayerSession::handleElementAdded(GstBin *bin, GstElement *elemen + // video sink doesn't support it + g_signal_connect(element, "autoplug-select", G_CALLBACK(handleAutoplugSelect), session); + } +- ++#endif + //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", +@@ -1711,68 +1773,30 @@ void QGstreamerPlayerSession::showPrerollFrames(bool enabled) + + void QGstreamerPlayerSession::addProbe(QGstreamerVideoProbeControl* probe) + { +- QMutexLocker locker(&m_videoProbeMutex); +- +- if (m_videoProbes.contains(probe)) +- return; +- +- m_videoProbes.append(probe); ++ Q_ASSERT(!m_videoProbe); ++ m_videoProbe = probe; ++ addVideoBufferProbe(); + } + + void QGstreamerPlayerSession::removeProbe(QGstreamerVideoProbeControl* probe) + { +- QMutexLocker locker(&m_videoProbeMutex); +- m_videoProbes.removeOne(probe); +- // Do not emit flush signal in this case. +- // Assume user releases any outstanding references to video frames. +-} +- +-gboolean QGstreamerPlayerSession::padVideoBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) +-{ +- Q_UNUSED(pad); +- +- QGstreamerPlayerSession *session = reinterpret_cast(user_data); +- QMutexLocker locker(&session->m_videoProbeMutex); +- +- if (session->m_videoProbes.isEmpty()) +- return TRUE; +- +- foreach (QGstreamerVideoProbeControl* probe, session->m_videoProbes) +- probe->bufferProbed(buffer); +- +- return TRUE; ++ Q_ASSERT(m_videoProbe == probe); ++ removeVideoBufferProbe(); ++ m_videoProbe = 0; + } + + void QGstreamerPlayerSession::addProbe(QGstreamerAudioProbeControl* probe) + { +- QMutexLocker locker(&m_audioProbeMutex); +- +- if (m_audioProbes.contains(probe)) +- return; +- +- m_audioProbes.append(probe); ++ Q_ASSERT(!m_audioProbe); ++ m_audioProbe = probe; ++ addAudioBufferProbe(); + } + + void QGstreamerPlayerSession::removeProbe(QGstreamerAudioProbeControl* probe) + { +- QMutexLocker locker(&m_audioProbeMutex); +- m_audioProbes.removeOne(probe); +-} +- +-gboolean QGstreamerPlayerSession::padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) +-{ +- Q_UNUSED(pad); +- +- QGstreamerPlayerSession *session = reinterpret_cast(user_data); +- QMutexLocker locker(&session->m_audioProbeMutex); +- +- if (session->m_audioProbes.isEmpty()) +- return TRUE; +- +- foreach (QGstreamerAudioProbeControl* probe, session->m_audioProbes) +- probe->bufferProbed(buffer); +- +- return TRUE; ++ Q_ASSERT(m_audioProbe == probe); ++ removeAudioBufferProbe(); ++ m_audioProbe = 0; + } + + // This function is similar to stop(), +@@ -1797,80 +1821,62 @@ void QGstreamerPlayerSession::endOfMediaReset() + + void QGstreamerPlayerSession::removeVideoBufferProbe() + { +- if (m_videoBufferProbeId == -1) ++ if (!m_videoProbe) + return; + +- if (!m_videoSink) { +- m_videoBufferProbeId = -1; +- return; +- } +- + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + if (pad) { +- gst_pad_remove_buffer_probe(pad, m_videoBufferProbeId); ++ m_videoProbe->removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } +- +- m_videoBufferProbeId = -1; + } + + void QGstreamerPlayerSession::addVideoBufferProbe() + { +- Q_ASSERT(m_videoBufferProbeId == -1); +- if (!m_videoSink) ++ if (!m_videoProbe) + return; + + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + if (pad) { +- m_videoBufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padVideoBufferProbe), this); ++ m_videoProbe->addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } + } + + void QGstreamerPlayerSession::removeAudioBufferProbe() + { +- if (m_audioBufferProbeId == -1) +- return; +- +- if (!m_audioSink) { +- m_audioBufferProbeId = -1; ++ if (!m_audioProbe) + return; +- } + + GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); + if (pad) { +- gst_pad_remove_buffer_probe(pad, m_audioBufferProbeId); ++ m_audioProbe->removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } +- +- m_audioBufferProbeId = -1; + } + + void QGstreamerPlayerSession::addAudioBufferProbe() + { +- Q_ASSERT(m_audioBufferProbeId == -1); +- if (!m_audioSink) ++ if (!m_audioProbe) + return; + + GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); + if (pad) { +- m_audioBufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padAudioBufferProbe), this); ++ m_audioProbe->addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); + } + } + + void QGstreamerPlayerSession::flushVideoProbes() + { +- QMutexLocker locker(&m_videoProbeMutex); +- foreach (QGstreamerVideoProbeControl* probe, m_videoProbes) +- probe->startFlushing(); ++ if (m_videoProbe) ++ m_videoProbe->startFlushing(); + } + + void QGstreamerPlayerSession::resumeVideoProbes() + { +- QMutexLocker locker(&m_videoProbeMutex); +- foreach (QGstreamerVideoProbeControl* probe, m_videoProbes) +- probe->stopFlushing(); ++ if (m_videoProbe) ++ m_videoProbe->stopFlushing(); + } + + void QGstreamerPlayerSession::playlistTypeFindFunction(GstTypeFind *find, gpointer userData) +@@ -1878,7 +1884,11 @@ void QGstreamerPlayerSession::playlistTypeFindFunction(GstTypeFind *find, gpoint + QGstreamerPlayerSession* session = (QGstreamerPlayerSession*)userData; + + const gchar *uri = 0; ++#if GST_CHECK_VERSION(1,0,0) ++ g_object_get(G_OBJECT(session->m_playbin), "current-uri", &uri, NULL); ++#else + g_object_get(G_OBJECT(session->m_playbin), "uri", &uri, NULL); ++#endif + + guint64 length = gst_type_find_get_length(find); + if (!length) +@@ -1887,7 +1897,7 @@ void QGstreamerPlayerSession::playlistTypeFindFunction(GstTypeFind *find, gpoint + length = qMin(length, guint64(1024)); + + while (length > 0) { +- guint8 *data = gst_type_find_peek(find, 0, length); ++ const guint8 *data = gst_type_find_peek(find, 0, length); + if (data) { + session->m_isPlaylist = (QPlaylistFileParser::findPlaylistType(QString::fromUtf8(uri), 0, data, length) != QPlaylistFileParser::UNKNOWN); + return; +diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h +index f2e760a..92b4a0c 100644 +--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h ++++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h +@@ -119,11 +119,9 @@ public: + + void addProbe(QGstreamerVideoProbeControl* probe); + void removeProbe(QGstreamerVideoProbeControl* probe); +- static gboolean padVideoBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); + + void addProbe(QGstreamerAudioProbeControl* probe); + void removeProbe(QGstreamerAudioProbeControl* probe); +- static gboolean padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); + + void endOfMediaReset(); + +@@ -172,7 +170,9 @@ 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); ++#if !GST_CHECK_VERSION(1,0,0) + static void insertColorSpaceElement(GstElement *element, gpointer data); ++#endif + 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); +@@ -194,11 +194,14 @@ private: + QGstreamerBusHelper* m_busHelper; + GstElement* m_playbin; + ++ GstElement* m_videoSink; ++ + GstElement* m_videoOutputBin; + GstElement* m_videoIdentity; ++#if !GST_CHECK_VERSION(1,0,0) + GstElement* m_colorSpace; + bool m_usingColorspaceElement; +- GstElement* m_videoSink; ++#endif + GstElement* m_pendingVideoSink; + GstElement* m_nullVideoSink; + +@@ -218,13 +221,8 @@ private: + QList m_streamTypes; + QMap m_playbin2StreamOffset; + +- QList m_videoProbes; +- QMutex m_videoProbeMutex; +- int m_videoBufferProbeId; +- +- QList m_audioProbes; +- QMutex m_audioProbeMutex; +- int m_audioBufferProbeId; ++ QGstreamerVideoProbeControl *m_videoProbe; ++ QGstreamerAudioProbeControl *m_audioProbe; + + int m_volume; + qreal m_playbackRate; +@@ -252,6 +250,7 @@ private: + bool m_isLiveSource; + + bool m_isPlaylist; ++ gulong pad_probe_id; + }; + + QT_END_NAMESPACE +diff --git a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp +index 344f1f5..2711ae0 100644 +--- a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp ++++ b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp +@@ -495,6 +495,8 @@ void tst_QCameraBackend::testCaptureToBuffer() + QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_Jpeg); + } + ++ QTRY_VERIFY(imageCapture.isReadyForCapture()); ++ + //Try to capture to both buffer and file + #ifdef Q_WS_MAEMO_6 + QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile)); +@@ -651,11 +653,11 @@ void tst_QCameraBackend::testVideoRecording() + { + QFETCH(QByteArray, device); + +- QCamera *camera = device.isEmpty() ? new QCamera : new QCamera(device); ++ QScopedPointer camera(device.isEmpty() ? new QCamera : new QCamera(device)); + +- QMediaRecorder recorder(camera); ++ QMediaRecorder recorder(camera.data()); + +- QSignalSpy errorSignal(camera, SIGNAL(error(QCamera::Error))); ++ QSignalSpy errorSignal(camera.data(), SIGNAL(error(QCamera::Error))); + QSignalSpy recorderErrorSignal(&recorder, SIGNAL(error(QMediaRecorder::Error))); + QSignalSpy recorderStatusSignal(&recorder, SIGNAL(statusChanged(QMediaRecorder::Status))); + +@@ -702,8 +704,6 @@ void tst_QCameraBackend::testVideoRecording() + camera->setCaptureMode(QCamera::CaptureStillImage); + QTRY_COMPARE(recorder.status(), QMediaRecorder::UnloadedStatus); + QCOMPARE(recorderStatusSignal.last().first().value(), recorder.status()); +- +- delete camera; + } + + QTEST_MAIN(tst_QCameraBackend) +diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp +index 0a1441c..ddf438b 100644 +--- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp ++++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp +@@ -724,7 +724,7 @@ void tst_QMediaPlayerBackend::seekPauseSeek() + + { + QVideoFrame frame = surface->m_frameList.back(); +- const qint64 elapsed = frame.startTime() - position; ++ const qint64 elapsed = (frame.startTime() / 1000) - position; // frame.startTime() is microsecond, position is milliseconds. + QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData()); + QCOMPARE(frame.width(), 160); + QCOMPARE(frame.height(), 120); +@@ -748,7 +748,7 @@ void tst_QMediaPlayerBackend::seekPauseSeek() + + { + QVideoFrame frame = surface->m_frameList.back(); +- const qint64 elapsed = frame.startTime() - position; ++ const qint64 elapsed = (frame.startTime() / 1000) - position; + QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData()); + QCOMPARE(frame.width(), 160); + QCOMPARE(frame.height(), 120); +-- +1.9.3 + diff --git a/qt5-qtmultimedia.spec b/qt5-qtmultimedia.spec index fa7117a..753fb47 100644 --- a/qt5-qtmultimedia.spec +++ b/qt5-qtmultimedia.spec @@ -11,7 +11,6 @@ %endif #define pre beta -%define snap 20141118 %define gst 0.10 %if 0%{?fedora} > 20 @@ -21,28 +20,25 @@ Summary: Qt5 - Multimedia support Name: qt5-%{qt_module} Version: 5.4.0 -Release: 0.3.%{snap}.gst1%{?dist} +Release: 1%{?dist} # See LGPL_EXCEPTIONS.txt, LICENSE.GPL3, respectively, for exception details License: LGPLv2 with exceptions or GPLv3 with exceptions Url: http://qt-project.org/ -%if 0%{?snap} -# snapshot from wip/gstreamer1 branch -Source0: qtmultimedia-opensource-src-5.4.0-20141118.gst1.tar.xz -%else %if 0%{?pre:1} Source0: http://download.qt-project.org/development_releases/qt/5.4/%{version}-%{pre}/submodules/%{qt_module}-opensource-src-%{version}-%{pre}.tar.xz %else Source0: http://download.qt-project.org/official_releases/qt/5.4/%{version}/submodules/%{qt_module}-opensource-src-%{version}.tar.xz %endif -%endif + +## upstream patches dev/ branch +# pull this in for now, may end up tracking dev/ branch snapshots when/if there are bugfixes +Patch106: 0006-GStreamer-port-to-1.0.patch BuildRequires: qt5-qtbase-devel >= %{version} BuildRequires: qt5-qtdeclarative-devel >= %{version} BuildRequires: pkgconfig(alsa) -%if "%{?gst}" == "1.0" -BuildConflicts: pkgconfig(gstreamer-0.10) -%else +%if "%{?gst}" == "0.10" BuildRequires: pkgconfig(gstreamer-interfaces-0.10) %endif BuildRequires: pkgconfig(gstreamer-%{gst}) @@ -96,6 +92,8 @@ Requires: %{name}%{?_isa} = %{version}-%{release} %prep %setup -n %{qt_module}-opensource-src-%{version}%{?pre:-%{pre}} +%patch106 -p1 -b .0006 + # force version match, snapshot is marked 5.5.0 currently sed -i -e "s|^MODULE_VERSION =.*|MODULE_VERSION = %{version}|" .qmake.conf @@ -108,7 +106,9 @@ sed -i -e "s|^MODULE_VERSION =.*|MODULE_VERSION = %{version}|" .qmake.conf %build mkdir %{_target_platform} pushd %{_target_platform} -%{qmake_qt5} .. %{?snap:CONFIG+=git_build} +%{qmake_qt5} .. \ + CONFIG+=git_build \ + GST_VERSION=%{gst} make %{?_smp_mflags} @@ -193,6 +193,9 @@ popd %changelog +* Thu Dec 11 2014 Rex Dieter 5.4.0-1 +- 5.4.0 (final) + backported gst1 support from dev/ branch + * Tue Nov 18 2014 Rex Dieter 5.4.0-0.3.20141118.gst1 - wip/gstreamer1 snapshot (#1149885) diff --git a/sources b/sources index beceb19..69c5717 100644 --- a/sources +++ b/sources @@ -1,2 +1 @@ -2b98f813ca26ea8447991ef2143f5e61 qtmultimedia-opensource-src-5.4.0-20141118.gst1.tar.xz -732d4872fd0ac601caa86267e9d366d0 qtmultimedia-opensource-src-5.4.0-beta.tar.xz +3a6a415813b54a54cf283a5e2906e012 qtmultimedia-opensource-src-5.4.0.tar.xz