Skip to contentSkip to conversation

qt5multimedia: compile fix without opengl and enable gstreamer-1.x support

PATCH New Peter Seiderer 3 participants 5 messages Jan 23, 2015 – Feb 3, 2015

lore
Share & tools
lore
#0 lore raw

v2 cover qt5multimedia: compile fix without opengl and enable gstreamer-1.x support

On top of Fatih A??c? patchset qt5: bump to version 5.4.0 ([1], [2] and [3]) a fix for compiling without opengl and a new version for enabling qt5multimedia gstreamer-1.x support.

Tested on Raspberry Pi B+ with a little aplication using the qt5multimedia framework for audio output (used buildroot config [4]), so

[1] https://patchwork.ozlabs.org/patch/431801/
[2] https://patchwork.ozlabs.org/patch/431803/
[3] https://patchwork.ozlabs.org/patch/431802/

[4] buildroot defconfig
-- begin --
BR2_arm=y
BR2_arm1176jzf_s=y
BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
BR2_GLIBC_VERSION_2_20=y
BR2_BINUTILS_VERSION_2_25=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_PACKAGE_HOST_GDB=y
BR2_PACKAGE_HOST_GDB_TUI=y
BR2_PACKAGE_HOST_GDB_PYTHON=y
BR2_GDB_VERSION_7_8=y
BR2_ENABLE_LOCALE_PURGE=y
BR2_INIT_SYSTEMD=y
BR2_TARGET_GENERIC_GETTY_PORT="tty1" BR2_ROOTFS_OVERLAY="board/raspberrypi/rootfs-overlay" BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="git://github.com/raspberrypi/linux.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="b9c9f8fc69aaba236169798c18dc1590fccf7acd" BR2_LINUX_KERNEL_PATCH="board/raspberrypi/linux-3.18" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/raspberrypi/linux-3.18-rapitopl.defconfig" BR2_LINUX_KERNEL_ZIMAGE=y
BR2_LINUX_KERNEL_EXT_FBTFT=y
BR2_PACKAGE_ALSA_UTILS=y
BR2_PACKAGE_ALSA_UTILS_ALSACONF=y
BR2_PACKAGE_ALSA_UTILS_APLAY=y
BR2_PACKAGE_ALSA_UTILS_SPEAKER_TEST=y
BR2_PACKAGE_GSTREAMER1=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_AUDIOTESTSRC=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_ALSA=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_OGG=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_VORBIS=y BR2_PACKAGE_GST1_PLUGINS_GOOD=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_AUDIOPARSERS=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_FLAC=y
BR2_PACKAGE_SOX=y
BR2_PACKAGE_STRACE=y
BR2_PACKAGE_QT5=y
BR2_PACKAGE_QT5BASE_LICENSE_APPROVED=y
BR2_PACKAGE_QT5BASE_WIDGETS=y
BR2_PACKAGE_QT5MULTIMEDIA=y
BR2_PACKAGE_DEJAVU=y
BR2_PACKAGE_LIBERATION=y
BR2_PACKAGE_RPI_FIRMWARE=y
BR2_PACKAGE_RPI_FIRMWARE_INSTALL_DTBS=y
BR2_PACKAGE_EVTEST=y
BR2_PACKAGE_I2C_TOOLS=y
BR2_PACKAGE_FONTCONFIG=y
BR2_PACKAGE_JPEG=y
BR2_PACKAGE_LIBPNG=y
BR2_PACKAGE_OPENSSH=y
-- end --

Peter Seiderer (2):
qt5multimedia: fix compile without opengl
qt5multimedia: enable gstreamer-1.x support

 ...ervideosurface-fix-compile-without-opengl.patch |   39 +
 .../qt5multimedia/0002-GStreamer-port-to-1.0.patch | 7649 ++++++++++++++++++++
 .../0003-Fix-GStreamer-port-to-1.0-compile.patch   |  121 +
 package/qt5/qt5multimedia/qt5multimedia.mk         |    6 +-
 4 files changed, 7814 insertions(+), 1 deletion(-)
 create mode 100644 package/qt5/qt5multimedia/0001-qpaintervideosurface-fix-compile-without-opengl.patch
 create mode 100644 package/qt5/qt5multimedia/0002-GStreamer-port-to-1.0.patch
 create mode 100644 package/qt5/qt5multimedia/0003-Fix-GStreamer-port-to-1.0-compile.patch
Signature · 1 line
2.1.2
#1 lore raw

PATCH v2 1/2 qt5multimedia: fix compile without opengl

---
 ...ervideosurface-fix-compile-without-opengl.patch | 39 ++++++++++++++++++++++
 1 file changed, 39 insertions(+)
 create mode 100644 package/qt5/qt5multimedia/0001-qpaintervideosurface-fix-compile-without-opengl.patch
package/qt5/qt5multimedia/0001-qpaintervideosurface-fix-compile-without-opengl.patch new file +39 -0
new file mode 100644
index 0000000..578b79e
@@ -0,0 +1,39 @@
+From bca9c8786da5b5bca47b873720cb0d576219d4a9 Mon Sep 17 00:00:00 2001
+From: Peter Seiderer <[email protected]>
+Date: Fri, 23 Jan 2015 18:58:29 +0100
+Subject: [PATCH] qpaintervideosurface: fix compile without opengl
+
+Signed-off-by: Peter Seiderer <[email protected]>
+---
+ src/multimediawidgets/qpaintervideosurface.cpp | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/multimediawidgets/qpaintervideosurface.cpp b/src/multimediawidgets/qpaintervideosurface.cpp
+index 3a880de..667ecd3 100644
+--- a/src/multimediawidgets/qpaintervideosurface.cpp
++++ b/src/multimediawidgets/qpaintervideosurface.cpp
+@@ -95,8 +95,10 @@ QVideoSurfaceGenericPainter::QVideoSurfaceGenericPainter()
+         << QVideoFrame::Format_RGB32
+         << QVideoFrame::Format_ARGB32
+         << QVideoFrame::Format_RGB565;
++#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+     // The raster formats should be a subset of the GL formats.
+     if (QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGLES)
++#endif
+         m_imagePixelFormats << QVideoFrame::Format_RGB24;
+ }
+ 
+@@ -137,8 +139,10 @@ QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::start(const QVideoSurf
+     const QAbstractVideoBuffer::HandleType t = format.handleType();
+     if (t == QAbstractVideoBuffer::NoHandle) {
+         bool ok = m_imageFormat != QImage::Format_Invalid && !m_imageSize.isEmpty();
++#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+         if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
+             ok &= format.pixelFormat() != QVideoFrame::Format_RGB24;
++#endif
+         if (ok)
+             return QAbstractVideoSurface::NoError;
+     } else if (t == QAbstractVideoBuffer::QPixmapHandle) {
+-- 
+2.1.2
+
Signature · 1 line
2.1.2
#2 lore raw

PATCH v2 1/2 qt5multimedia: fix compile without opengl

Dear Peter Seiderer,

Quoted 29 lines · Peter Seiderer
package/qt5/qt5multimedia/0001-qpaintervideosurface-fix-compile-without-opengl.patch new file +23 -0
new file mode 100644
index 0000000..578b79e
@@ -0,0 +1,39 @@
+From bca9c8786da5b5bca47b873720cb0d576219d4a9 Mon Sep 17 00:00:00 2001
+From: Peter Seiderer <[email protected]>
+Date: Fri, 23 Jan 2015 18:58:29 +0100
+Subject: [PATCH] qpaintervideosurface: fix compile without opengl
+
+Signed-off-by: Peter Seiderer <[email protected]>
+---
+ src/multimediawidgets/qpaintervideosurface.cpp | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/multimediawidgets/qpaintervideosurface.cpp b/src/multimediawidgets/qpaintervideosurface.cpp
+index 3a880de..667ecd3 100644
+--- a/src/multimediawidgets/qpaintervideosurface.cpp
++++ b/src/multimediawidgets/qpaintervideosurface.cpp
+@@ -95,8 +95,10 @@ QVideoSurfaceGenericPainter::QVideoSurfaceGenericPainter()
+         << QVideoFrame::Format_RGB32
+         << QVideoFrame::Format_ARGB32
+         << QVideoFrame::Format_RGB565;
++#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+     // The raster formats should be a subset of the GL formats.
+     if (QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGLES)
++#endif
+         m_imagePixelFormats << QVideoFrame::Format_RGB24;

package/qt5/qt5multimedia/0001-qpaintervideosurface-fix-compile-without-opengl.patch line 33

Quoted 10 lines
package/qt5/qt5multimedia/0001-qpaintervideosurface-fix-compile-without-opengl.patch new file +10 -0
+ }
+ 
+@@ -137,8 +139,10 @@ QAbstractVideoSurface::Error QVideoSurfaceGenericPainter::start(const QVideoSurf
+     const QAbstractVideoBuffer::HandleType t = format.handleType();
+     if (t == QAbstractVideoBuffer::NoHandle) {
+         bool ok = m_imageFormat != QImage::Format_Invalid && !m_imageSize.isEmpty();
++#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_1_CL) && !defined(QT_OPENGL_ES_1)
+         if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
+             ok &= format.pixelFormat() != QVideoFrame::Format_RGB24;
++#endif

This is a bit scarce on details, and the first hunk that just removes the if condition looks a bit suspicious. Could we instead get a patch accepted upstream?

Thanks,

Thomas

Signature · 3 lines
Thomas Petazzoni, CTO, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
#3 lore raw

PATCH v2 2/2 qt5multimedia: enable gstreamer-1.x support

In reply to Peter Seiderer

Add 0002-GStreamer-port-to-1.0.patch [1] from upstream qtmultimedia dev branch (will be in qt-5.5) and 0003-Fix-GStreamer-port-to-1.0-compile.patch to fix resulting compile failures (private header related).

Fix qt5multimedia.mk for the gstreamer-1.x case (gst-plugins-base and gst1-plugins-base).

[1] https://qt.gitorious.org/qt/qtmultimedia/commit/108dda7a90bd0f0337358b0db47ae55acd16dea6

---
Changes v1 --> v2:
- rebased on top of qt5: bump to version 5.4.0
- changed old gstreamer-1.x porting patches against newer upstream committed one

---
 .../qt5multimedia/0002-GStreamer-port-to-1.0.patch | 7649 ++++++++++++++++++++
 .../0003-Fix-GStreamer-port-to-1.0-compile.patch   |  121 +
 package/qt5/qt5multimedia/qt5multimedia.mk         |    6 +-
 3 files changed, 7775 insertions(+), 1 deletion(-)
 create mode 100644 package/qt5/qt5multimedia/0002-GStreamer-port-to-1.0.patch
 create mode 100644 package/qt5/qt5multimedia/0003-Fix-GStreamer-port-to-1.0-compile.patch
package/qt5/qt5multimedia/0002-GStreamer-port-to-1.0.patch new file +7649 -0
new file mode 100644
index 0000000..a1aad07
@@ -0,0 +1,7649 @@
+From 2fa8dc9f6a94113e3fae3af21e9937f8209fc865 Mon Sep 17 00:00:00 2001
+From: Yoann Lopes <[email protected]>
+Date: Thu, 20 Nov 2014 17:54:18 +0100
+Subject: [PATCH] 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 <[email protected]>
+Ilya Smelykh <[email protected]>
+Jim Hodapp <[email protected]>
+Sergio Schvezov <[email protected]>
+
+Change-Id: I72a46d1170a8794a149bdb5e20767afcc5b7587c
+Reviewed-by: Andrew den Exter <[email protected]>
+Signed-off-by: Peter Seiderer <[email protected]>
+---
+ 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 148366b..57eaacd 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 <QtCore/qset.h>
+ 
+ #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<const char *>(info.data), info.size);
++        gst_buffer_unmap(buffer, &info);
++    } else {
++        return true;
++    }
++#else
++    data = QByteArray(reinterpret_cast<const char *>(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<QGstreamerBufferProbe *>(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<QGstreamerBufferProbe *>(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<QGstreamerBufferProbe *>(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 <qgstreamerplayersession.h>
++#include <private/qvideosurfacegstsink_p.h>
++#include <private/qgstutils_p.h>
++#include <qabstractvideosurface.h>
++
++#include <QAbstractVideoBuffer>
++#include <QGuiApplication>
++#include <QDebug>
++#include <QtQuick/QQuickWindow>
++#include <QOpenGLContext>
++#include <QGLContext>
++#include <QGuiApplication>
++#include <qgl.h>
++
++#include <gst/gst.h>
++
++static QGstreamerMirTextureRenderer *rendererInstance = NULL;
++
++class QGstreamerMirTextureBuffer : public QAbstractVideoBuffer
++{
++public:
++    QGstreamerMirTextureBuffer(GLuint textureId) :
++        QAbstractVideoBuffer(QAbstractVideoBuffer::GLTextureHandle),
++        m_textureId(textureId)
++    {
++    }
++
++    MapMode mapMode() const { return NotMapped; }
++
++    uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
++    {
++        qDebug() << Q_FUNC_INFO;
++        Q_UNUSED(mode);
++        Q_UNUSED(numBytes);
++        Q_UNUSED(bytesPerLine);
++
++        return NULL;
++    }
++
++    void unmap() { qDebug() << Q_FUNC_INFO; }
++
++    QVariant handle() const { return QVariant::fromValue<unsigned int>(m_textureId); }
++
++    GLuint textureId() { return m_textureId; }
++
++private:
++    GLuint m_textureId;
++};
++
++QGstreamerMirTextureRenderer::QGstreamerMirTextureRenderer(QObject *parent
++        , const QGstreamerPlayerSession *playerSession)
++    : QVideoRendererControl(0), m_videoSink(0), m_surface(0),
++      m_glSurface(0),
++      m_context(0),
++      m_glContext(0),
++      m_textureId(0),
++      m_offscreenSurface(0),
++      m_textureBuffer(0)
++{
++    Q_UNUSED(parent);
++    setPlayerSession(playerSession);
++}
++
++QGstreamerMirTextureRenderer::~QGstreamerMirTextureRenderer()
++{
++    if (m_videoSink)
++        gst_object_unref(GST_OBJECT(m_videoSink));
++
++    delete m_glContext;
++    delete m_offscreenSurface;
++}
++
++GstElement *QGstreamerMirTextureRenderer::videoSink()
++{
++    qDebug() << Q_FUNC_INFO;
++
++    // FIXME: Ugly hack until I figure out why passing this segfaults in the g_signal handler
++    rendererInstance = const_cast<QGstreamerMirTextureRenderer*>(this);
++
++    if (!m_videoSink && m_surface) {
++        qDebug() << Q_FUNC_INFO << ": using mirsink, (this: " << this << ")";
++
++        m_videoSink = gst_element_factory_make("mirsink", "video-output");
++
++        connect(QGuiApplication::instance(), SIGNAL(focusWindowChanged(QWindow*)),
++                this, SLOT(handleFocusWindowChanged(QWindow*)), Qt::QueuedConnection);
++
++        g_signal_connect(G_OBJECT(m_videoSink), "frame-ready", G_CALLBACK(handleFrameReady),
++                (gpointer)this);
++    }
++
++    if (m_videoSink) {
++        gst_object_ref_sink(GST_OBJECT(m_videoSink));
++
++        GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink");
++        gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER,
++                padBufferProbe, this, NULL);
++    }
++
++    return m_videoSink;
++}
++
++QWindow *QGstreamerMirTextureRenderer::createOffscreenWindow(const QSurfaceFormat &format)
++{
++    QWindow *w = new QWindow();
++    w->setSurfaceType(QWindow::OpenGLSurface);
++    w->setFormat(format);
++    w->setGeometry(0, 0, 1, 1);
++    w->setFlags(w->flags() | Qt::WindowTransparentForInput);
++    w->create();
++
++    return w;
++}
++
++void QGstreamerMirTextureRenderer::handleFrameReady(gpointer userData)
++{
++    QGstreamerMirTextureRenderer *renderer = reinterpret_cast<QGstreamerMirTextureRenderer*>(userData);
++#if 1
++    QMutexLocker locker(&rendererInstance->m_mutex);
++    QMetaObject::invokeMethod(rendererInstance, "renderFrame", Qt::QueuedConnection);
++#else
++    // FIXME!
++    //QMutexLocker locker(&renderer->m_mutex);
++    QMetaObject::invokeMethod(renderer, "renderFrame", Qt::QueuedConnection);
++#endif
++}
++
++void QGstreamerMirTextureRenderer::renderFrame()
++{
++    //qDebug() << Q_FUNC_INFO;
++
++    if (m_context)
++        m_context->makeCurrent();
++
++    GstState pendingState = GST_STATE_NULL;
++    GstState newState = GST_STATE_NULL;
++    // Don't block and return immediately:
++    GstStateChangeReturn ret = gst_element_get_state(m_videoSink, &newState,
++                                                     &pendingState, 0);
++    if (ret == GST_STATE_CHANGE_FAILURE || newState == GST_STATE_NULL||
++            pendingState == GST_STATE_NULL) {
++        qWarning() << "Invalid state change for renderer, aborting";
++        stopRenderer();
++        return;
++    }
++
++    if (!m_surface->isActive()) {
++        qDebug() << "m_surface is not active";
++        GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink");
++        GstCaps *caps = gst_pad_get_current_caps(pad);
++
++        if (caps) {
++            // Get the native video size from the video sink
++            QSize newNativeSize = QGstUtils::capsCorrectedResolution(caps);
++            if (m_nativeSize != newNativeSize) {
++                m_nativeSize = newNativeSize;
++                emit nativeSizeChanged();
++            }
++            gst_caps_unref(caps);
++        }
++
++        // Start the surface
++        QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle);
++        qDebug() << "m_nativeSize: " << m_nativeSize;
++        qDebug() << "format: " << format;
++        if (!m_surface->start(format)) {
++            qWarning() << Q_FUNC_INFO << ": failed to start the video surface " << format;
++            return;
++        }
++    }
++
++    QGstreamerMirTextureBuffer *buffer = new QGstreamerMirTextureBuffer(m_textureId);
++    //qDebug() << "frameSize: " << m_surface->surfaceFormat().frameSize();
++    QVideoFrame frame(buffer, m_surface->surfaceFormat().frameSize(),
++                      m_surface->surfaceFormat().pixelFormat());
++
++    frame.setMetaData("TextureId", m_textureId);
++
++    // Display the video frame on the surface:
++    m_surface->present(frame);
++}
++
++GstPadProbeReturn QGstreamerMirTextureRenderer::padBufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer userData)
++{
++    Q_UNUSED(pad);
++    Q_UNUSED(info);
++
++    QGstreamerMirTextureRenderer *control = reinterpret_cast<QGstreamerMirTextureRenderer*>(userData);
++    QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection);
++
++    return GST_PAD_PROBE_REMOVE;
++}
++
++void QGstreamerMirTextureRenderer::stopRenderer()
++{
++    if (m_surface)
++        m_surface->stop();
++}
++
++QAbstractVideoSurface *QGstreamerMirTextureRenderer::surface() const
++{
++    return m_surface;
++}
++
++void QGstreamerMirTextureRenderer::setSurface(QAbstractVideoSurface *surface)
++{
++    qDebug() << Q_FUNC_INFO;
++
++    if (m_surface != surface) {
++        qDebug() << "Saving current QGLContext";
++        m_context = const_cast<QGLContext*>(QGLContext::currentContext());
++
++        if (m_videoSink)
++            gst_object_unref(GST_OBJECT(m_videoSink));
++
++        m_videoSink = 0;
++
++        if (m_surface) {
++            disconnect(m_surface.data(), SIGNAL(supportedFormatsChanged()),
++                       this, SLOT(handleFormatChange()));
++        }
++
++        bool wasReady = isReady();
++
++        m_surface = surface;
++
++        if (m_surface) {
++            connect(m_surface.data(), SIGNAL(supportedFormatsChanged()),
++                    this, SLOT(handleFormatChange()));
++        }
++
++        if (wasReady != isReady())
++            emit readyChanged(isReady());
++
++        emit sinkChanged();
++    }
++}
++
++void QGstreamerMirTextureRenderer::setPlayerSession(const QGstreamerPlayerSession *playerSession)
++{
++    m_playerSession = const_cast<QGstreamerPlayerSession*>(playerSession);
++}
++
++void QGstreamerMirTextureRenderer::handleFormatChange()
++{
++    qDebug() << "Supported formats list has changed, reload video output";
++
++    if (m_videoSink)
++        gst_object_unref(GST_OBJECT(m_videoSink));
++
++    m_videoSink = 0;
++    emit sinkChanged();
++}
++
++void QGstreamerMirTextureRenderer::updateNativeVideoSize()
++{
++    //qDebug() << Q_FUNC_INFO;
++    const QSize oldSize = m_nativeSize;
++
++    if (m_videoSink) {
++        // Find video native size to update video widget size hint
++        GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink");
++        GstCaps *caps = gst_pad_get_current_caps(pad);
++
++        if (caps) {
++            m_nativeSize = QGstUtils::capsCorrectedResolution(caps);
++            gst_caps_unref(caps);
++        }
++    } else {
++        m_nativeSize = QSize();
++    }
++    qDebug() << Q_FUNC_INFO << oldSize << m_nativeSize << m_videoSink;
++
++    if (m_nativeSize != oldSize)
++        emit nativeSizeChanged();
++}
++
++void QGstreamerMirTextureRenderer::handleFocusWindowChanged(QWindow *window)
++{
++    qDebug() << Q_FUNC_INFO;
++
++    QOpenGLContext *currContext = QOpenGLContext::currentContext();
++
++    QQuickWindow *w = dynamic_cast<QQuickWindow*>(window);
++    // If we don't have a GL context in the current thread, create one and share it
++    // with the render thread GL context
++    if (!currContext && !m_glContext) {
++        // This emulates the new QOffscreenWindow class with Qt5.1
++        m_offscreenSurface = createOffscreenWindow(w->openglContext()->surface()->format());
++        m_offscreenSurface->setParent(window);
++
++        QOpenGLContext *shareContext = 0;
++        if (m_surface)
++            shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>());
++        m_glContext = new QOpenGLContext;
++        m_glContext->setFormat(m_offscreenSurface->requestedFormat());
++
++        if (shareContext)
++            m_glContext->setShareContext(shareContext);
++
++        if (!m_glContext->create())
++        {
++            qWarning() << "Failed to create new shared context.";
++            return;
++        }
++    }
++
++    if (m_glContext)
++        m_glContext->makeCurrent(m_offscreenSurface);
++
++    if (m_textureId == 0) {
++        glGenTextures(1, &m_textureId);
++        qDebug() << "texture_id (handleFocusWindowChanged): " << m_textureId << endl;
++        g_object_set(G_OBJECT(m_videoSink), "texture-id", m_textureId, (char*)NULL);
++    }
++}
+diff --git a/src/gsttools/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 <private/qvideosurfacegstsink_p.h>
++
++#include "qgstutils_p.h"
+ #include <private/qgstvideobuffer_p.h>
+ 
+ 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 <private/qvideosurfacegstsink_p.h>
+ #include <private/qgstutils_p.h>
+ #include <qabstractvideosurface.h>
+-
+-#include <QDebug>
++#include <QtCore/qdebug.h>
+ 
+ #include <gst/gst.h>
+ 
+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 <QtGui/qpainter.h>
+ 
+ #include <gst/gst.h>
++
++#if !GST_CHECK_VERSION(1,0,0)
+ #include <gst/interfaces/xoverlay.h>
+ #include <gst/interfaces/propertyprobe.h>
++#else
++#include <gst/video/videooverlay.h>
++#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 <QtCore/qdebug.h>
+ 
+ #include <gst/gst.h>
++
++#if !GST_CHECK_VERSION(1,0,0)
+ #include <gst/interfaces/xoverlay.h>
+ #include <gst/interfaces/propertyprobe.h>
++#else
++#include <gst/video/videooverlay.h>
++#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<QGstreamerVideoWindow*>(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 <QtCore/qsize.h>
+ #include <QtCore/qset.h>
+ #include <QtCore/qstringlist.h>
++#include <QtGui/qimage.h>
+ #include <qaudioformat.h>
++#include <QtMultimedia/qvideosurfaceformat.h>
++
++#include <gst/audio/audio.h>
++#include <gst/video/video.h>
++
++template<typename T, int N> static int lengthOf(const T (&)[N]) { return N; }
+ 
+ #ifdef USE_V4L
+ #  include <private/qcore_unix_p.h>
+@@ -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<QString> QGstUtils::supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory))
++{
++    QSet<QString> 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<GstStaticPadTemplate *>(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<const uchar *>(frame.data[0]),
++            static_cast<const uchar *>(frame.data[1]),
++            static_cast<const uchar *>(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; y<height; y+=2) {
++            const uchar *yLine = data[0] + (y * stride[0]);
++            const uchar *uLine = data[1] + (y * stride[1] / 2);
++            const uchar *vLine = data[2] + (y * stride[2] / 2);
++
++            for (int x=0; x<width; x+=2) {
++                const qreal Y = 1.164*(yLine[x]-16);
++                const int U = uLine[x/2]-128;
++                const int V = vLine[x/2]-128;
++
++                int b = qBound(0, int(Y + 2.018*U), 255);
++                int g = qBound(0, int(Y - 0.813*V - 0.391*U), 255);
++                int r = qBound(0, int(Y + 1.596*V), 255);
++
++                img.setPixel(x/2,y/2,qRgb(r,g,b));
++            }
++        }
++#if GST_CHECK_VERSION(1,0,0)
++    } else for (int i = 0; i < lengthOf(qt_colorLookup); ++i) {
++        if (qt_colorLookup[i].gstFormat != videoInfo.finfo->format)
++            continue;
++
++        const QImage image(
++                    static_cast<const uchar *>(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<int, int> 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<QVideoFrame::PixelFormat> &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<QByteArray, QVariant> &data)
++{
++    if (!GST_IS_TAG_SETTER(element))
++        return;
++
++    gst_tag_setter_reset_tags(GST_TAG_SETTER(element));
++
++    QMapIterator<QByteArray, QVariant> 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<QByteArray, QVariant> &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<uchar *>(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<uchar *>(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 <qabstractvideosurface.h>
++#include <qvideoframe.h>
++#include <QDebug>
++#include <QMap>
++#include <QThread>
++#include <QEvent>
++#include <QCoreApplication>
++
++#include <private/qmediapluginloader_p.h>
++#include "qgstvideobuffer_p.h"
++
++#include "qgstvideorenderersink_p.h"
++
++#include <gst/video/video.h>
++
++#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<QGstVideoRendererInterface*>(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<QVideoSurfaceGstDelegate *>(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);
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

package/qt5/qt5multimedia/0003-Fix-GStreamer-port-to-1.0-compile.patch new file +121 -0

121 lines not on this page · raw message

package/qt5/qt5multimedia/qt5multimedia.mk +5 -1
index 0c8d2ec..db52a7e 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.1.2

Part of the diff is not shown here. View the raw message.

#4 lore raw

PATCH v2 2/2 qt5multimedia: enable gstreamer-1.x support

Quoted 10 lines · Peter Seiderer

Add 0002-GStreamer-port-to-1.0.patch [1] from upstream qtmultimedia dev branch (will be in qt-5.5) and 0003-Fix-GStreamer-port-to-1.0-compile.patch to fix resulting compile failures (private header related).

Fix qt5multimedia.mk for the gstreamer-1.x case (gst-plugins-base and gst1-plugins-base).

[1] https://qt.gitorious.org/qt/qtmultimedia/commit/108dda7a90bd0f0337358b0db47ae55acd16dea6

Hi Peter,

Since this is a feature patch, and it will be included in an official qt
release a couple of months from now, we're not going to take it.

It's good that you posted it on the list, however, so other people can use it.
Also, it will be useful for updating the .mk file when qt-5.5 has been released.

Regards,
Arnout

Signature · 6 lines
Arnout Vandecappelle                          arnout at mind be
Senior Embedded Software Architect            +32-16-286500
Essensium/Mind                                http://www.mind.be
G.Geenslaan 9, 3001 Leuven, Belgium           BE 872 984 063 RPR Leuven
LinkedIn profile: http://www.linkedin.com/in/arnoutvandecappelle
GPG fingerprint:  7CB5 E4CC 6C2E EFD4 6E3D A754 F963 ECAB 2450 2F1F