From 108dda7a90bd0f0337358b0db47ae55acd16dea6 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Thu, 20 Nov 2014 17:54:18 +0100 Subject: GStreamer: port to 1.0. 0.10 is still used by default. To enable GStreamer 1.0, pass GST_VERSION=1.0 to qmake for qtmultimedia.pro. Contributions from: Andrew den Exter Ilya Smelykh Jim Hodapp Sergio Schvezov Change-Id: I72a46d1170a8794a149bdb5e20767afcc5b7587c Reviewed-by: Andrew den Exter --- 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 +- 63 files changed, 3609 insertions(+), 1332 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 (limited to 'src') diff --git a/src/gsttools/gsttools.pro b/src/gsttools/gsttools.pro index 7c809a777..7c41f1ad1 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 561a96fc8..178e11805 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 f584fbe69..888722aee 100644 --- a/src/gsttools/qgstcodecsinfo.cpp +++ b/src/gsttools/qgstcodecsinfo.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include "qgstcodecsinfo_p.h" - +#include "qgstutils_p.h" #include #ifdef QMEDIA_GSTREAMER_CAMERABIN @@ -146,7 +146,7 @@ GstCaps* QGstCodecsInfo::supportedElementCaps(GstElementFactoryListType elementT if (fakeEncoderMimeTypes.contains(gst_structure_get_name(structure))) continue; - GstStructure *newStructure = gst_structure_new(gst_structure_get_name(structure), NULL); + GstStructure *newStructure = qt_gst_structure_new_empty(gst_structure_get_name(structure)); //add structure fields to distinguish between formats with similar mime types, //like audio/mpeg @@ -166,7 +166,11 @@ GstCaps* QGstCodecsInfo::supportedElementCaps(GstElementFactoryListType elementT } } +#if GST_CHECK_VERSION(1,0,0) + res = +#endif gst_caps_merge_structure(res, newStructure); + } gst_caps_unref(caps); } diff --git a/src/gsttools/qgstreameraudioprobecontrol.cpp b/src/gsttools/qgstreameraudioprobecontrol.cpp index 3baca530f..9670d0f19 100644 --- a/src/gsttools/qgstreameraudioprobecontrol.cpp +++ b/src/gsttools/qgstreameraudioprobecontrol.cpp @@ -37,32 +37,48 @@ QGstreamerAudioProbeControl::QGstreamerAudioProbeControl(QObject *parent) : QMediaAudioProbeControl(parent) { - } QGstreamerAudioProbeControl::~QGstreamerAudioProbeControl() { - } -void QGstreamerAudioProbeControl::bufferProbed(GstBuffer* buffer) +void QGstreamerAudioProbeControl::probeCaps(GstCaps *caps) { - GstCaps* caps = gst_buffer_get_caps(buffer); - if (!caps) - return; - QAudioFormat format = QGstUtils::audioFormatForCaps(caps); - gst_caps_unref(caps); - if (!format.isValid()) - return; - QAudioBuffer audioBuffer = QAudioBuffer(QByteArray((const char*)buffer->data, buffer->size), format); + QMutexLocker locker(&m_bufferMutex); + m_format = format; +} - { - QMutexLocker locker(&m_bufferMutex); - m_pendingBuffer = audioBuffer; - QMetaObject::invokeMethod(this, "bufferProbed", Qt::QueuedConnection); +bool QGstreamerAudioProbeControl::probeBuffer(GstBuffer *buffer) +{ + qint64 position = GST_BUFFER_TIMESTAMP(buffer); + position = position >= 0 + ? position / G_GINT64_CONSTANT(1000) // microseconds + : -1; + + QByteArray data; +#if GST_CHECK_VERSION(1,0,0) + GstMapInfo info; + if (gst_buffer_map(buffer, &info, GST_MAP_READ)) { + data = QByteArray(reinterpret_cast(info.data), info.size); + gst_buffer_unmap(buffer, &info); + } else { + return true; + } +#else + data = QByteArray(reinterpret_cast(buffer->data), buffer->size); +#endif + + QMutexLocker locker(&m_bufferMutex); + if (m_format.isValid()) { + if (!m_pendingBuffer.isValid()) + QMetaObject::invokeMethod(this, "bufferProbed", Qt::QueuedConnection); + m_pendingBuffer = QAudioBuffer(data, m_format, position); } + + return true; } void QGstreamerAudioProbeControl::bufferProbed() @@ -73,6 +89,7 @@ void QGstreamerAudioProbeControl::bufferProbed() if (!m_pendingBuffer.isValid()) return; audioBuffer = m_pendingBuffer; + m_pendingBuffer = QAudioBuffer(); } emit audioBufferProbed(audioBuffer); } diff --git a/src/gsttools/qgstreamerbufferprobe.cpp b/src/gsttools/qgstreamerbufferprobe.cpp new file mode 100644 index 000000000..91f81268c --- /dev/null +++ b/src/gsttools/qgstreamerbufferprobe.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerbufferprobe_p.h" +#include "qgstutils_p.h" + +QT_BEGIN_NAMESPACE + +QGstreamerBufferProbe::QGstreamerBufferProbe(Flags flags) +#if GST_CHECK_VERSION(1,0,0) + : m_capsProbeId(-1) +#else + : m_caps(0) +#endif + , m_bufferProbeId(-1) + , m_flags(flags) +{ +} + +QGstreamerBufferProbe::~QGstreamerBufferProbe() +{ +#if !GST_CHECK_VERSION(1,0,0) + if (m_caps) + gst_caps_unref(m_caps); +#endif +} + +void QGstreamerBufferProbe::addProbeToPad(GstPad *pad, bool downstream) +{ + if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) { + probeCaps(caps); + gst_caps_unref(caps); + } +#if GST_CHECK_VERSION(1,0,0) + if (m_flags & ProbeCaps) { + m_capsProbeId = gst_pad_add_probe( + pad, + downstream + ? GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM + : GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, + capsProbe, + this, + NULL); + } + if (m_flags & ProbeBuffers) { + m_bufferProbeId = gst_pad_add_probe( + pad, GST_PAD_PROBE_TYPE_BUFFER, bufferProbe, this, NULL); + } +#else + Q_UNUSED(downstream); + + m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(bufferProbe), this); +#endif +} + +void QGstreamerBufferProbe::removeProbeFromPad(GstPad *pad) +{ +#if GST_CHECK_VERSION(1,0,0) + if (m_capsProbeId != -1) { + gst_pad_remove_probe(pad, m_capsProbeId); + m_capsProbeId = -1; + } + if (m_bufferProbeId != -1) { + gst_pad_remove_probe(pad, m_bufferProbeId); + m_bufferProbeId = -1; + } +#else + if (m_bufferProbeId != -1) { + gst_pad_remove_buffer_probe(pad, m_bufferProbeId); + m_bufferProbeId = -1; + if (m_caps) { + gst_caps_unref(m_caps); + m_caps = 0; + } + } +#endif +} + +void QGstreamerBufferProbe::probeCaps(GstCaps *) +{ +} + +bool QGstreamerBufferProbe::probeBuffer(GstBuffer *) +{ + return true; +} + +#if GST_CHECK_VERSION(1,0,0) +GstPadProbeReturn QGstreamerBufferProbe::capsProbe( + GstPad *, GstPadProbeInfo *info, gpointer user_data) +{ + QGstreamerBufferProbe * const control = static_cast(user_data); + + if (GstEvent * const event = gst_pad_probe_info_get_event(info)) { + if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS) { + GstCaps *caps; + gst_event_parse_caps(event, &caps); + + control->probeCaps(caps); + } + } + return GST_PAD_PROBE_OK; +} + +GstPadProbeReturn QGstreamerBufferProbe::bufferProbe( + GstPad *, GstPadProbeInfo *info, gpointer user_data) +{ + QGstreamerBufferProbe * const control = static_cast(user_data); + if (GstBuffer * const buffer = gst_pad_probe_info_get_buffer(info)) + return control->probeBuffer(buffer) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP; + return GST_PAD_PROBE_OK; +} +#else +gboolean QGstreamerBufferProbe::bufferProbe(GstElement *, GstBuffer *buffer, gpointer user_data) +{ + QGstreamerBufferProbe * const control = static_cast(user_data); + + if (control->m_flags & ProbeCaps) { + GstCaps *caps = gst_buffer_get_caps(buffer); + if (caps && (!control->m_caps || !gst_caps_is_equal(control->m_caps, caps))) { + qSwap(caps, control->m_caps); + control->probeCaps(control->m_caps); + } + if (caps) + gst_caps_unref(caps); + } + + if (control->m_flags & ProbeBuffers) { + return control->probeBuffer(buffer) ? TRUE : FALSE; + } else { + return TRUE; + } +} +#endif + +QT_END_NAMESPACE diff --git a/src/gsttools/qgstreamerbushelper.cpp b/src/gsttools/qgstreamerbushelper.cpp index 84eda46fc..eb1fc36f6 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 000000000..39e0db7ce --- /dev/null +++ b/src/gsttools/qgstreamermirtexturerenderer.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamermirtexturerenderer_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static QGstreamerMirTextureRenderer *rendererInstance = NULL; + +class QGstreamerMirTextureBuffer : public QAbstractVideoBuffer +{ +public: + QGstreamerMirTextureBuffer(GLuint textureId) : + QAbstractVideoBuffer(QAbstractVideoBuffer::GLTextureHandle), + m_textureId(textureId) + { + } + + MapMode mapMode() const { return NotMapped; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + qDebug() << Q_FUNC_INFO; + Q_UNUSED(mode); + Q_UNUSED(numBytes); + Q_UNUSED(bytesPerLine); + + return NULL; + } + + void unmap() { qDebug() << Q_FUNC_INFO; } + + QVariant handle() const { return QVariant::fromValue(m_textureId); } + + GLuint textureId() { return m_textureId; } + +private: + GLuint m_textureId; +}; + +QGstreamerMirTextureRenderer::QGstreamerMirTextureRenderer(QObject *parent + , const QGstreamerPlayerSession *playerSession) + : QVideoRendererControl(0), m_videoSink(0), m_surface(0), + m_glSurface(0), + m_context(0), + m_glContext(0), + m_textureId(0), + m_offscreenSurface(0), + m_textureBuffer(0) +{ + Q_UNUSED(parent); + setPlayerSession(playerSession); +} + +QGstreamerMirTextureRenderer::~QGstreamerMirTextureRenderer() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + delete m_glContext; + delete m_offscreenSurface; +} + +GstElement *QGstreamerMirTextureRenderer::videoSink() +{ + qDebug() << Q_FUNC_INFO; + + // FIXME: Ugly hack until I figure out why passing this segfaults in the g_signal handler + rendererInstance = const_cast(this); + + if (!m_videoSink && m_surface) { + qDebug() << Q_FUNC_INFO << ": using mirsink, (this: " << this << ")"; + + m_videoSink = gst_element_factory_make("mirsink", "video-output"); + + connect(QGuiApplication::instance(), SIGNAL(focusWindowChanged(QWindow*)), + this, SLOT(handleFocusWindowChanged(QWindow*)), Qt::QueuedConnection); + + g_signal_connect(G_OBJECT(m_videoSink), "frame-ready", G_CALLBACK(handleFrameReady), + (gpointer)this); + } + + if (m_videoSink) { + gst_object_ref_sink(GST_OBJECT(m_videoSink)); + + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, + padBufferProbe, this, NULL); + } + + return m_videoSink; +} + +QWindow *QGstreamerMirTextureRenderer::createOffscreenWindow(const QSurfaceFormat &format) +{ + QWindow *w = new QWindow(); + w->setSurfaceType(QWindow::OpenGLSurface); + w->setFormat(format); + w->setGeometry(0, 0, 1, 1); + w->setFlags(w->flags() | Qt::WindowTransparentForInput); + w->create(); + + return w; +} + +void QGstreamerMirTextureRenderer::handleFrameReady(gpointer userData) +{ + QGstreamerMirTextureRenderer *renderer = reinterpret_cast(userData); +#if 1 + QMutexLocker locker(&rendererInstance->m_mutex); + QMetaObject::invokeMethod(rendererInstance, "renderFrame", Qt::QueuedConnection); +#else + // FIXME! + //QMutexLocker locker(&renderer->m_mutex); + QMetaObject::invokeMethod(renderer, "renderFrame", Qt::QueuedConnection); +#endif +} + +void QGstreamerMirTextureRenderer::renderFrame() +{ + //qDebug() << Q_FUNC_INFO; + + if (m_context) + m_context->makeCurrent(); + + GstState pendingState = GST_STATE_NULL; + GstState newState = GST_STATE_NULL; + // Don't block and return immediately: + GstStateChangeReturn ret = gst_element_get_state(m_videoSink, &newState, + &pendingState, 0); + if (ret == GST_STATE_CHANGE_FAILURE || newState == GST_STATE_NULL|| + pendingState == GST_STATE_NULL) { + qWarning() << "Invalid state change for renderer, aborting"; + stopRenderer(); + return; + } + + if (!m_surface->isActive()) { + qDebug() << "m_surface is not active"; + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + GstCaps *caps = gst_pad_get_current_caps(pad); + + if (caps) { + // Get the native video size from the video sink + QSize newNativeSize = QGstUtils::capsCorrectedResolution(caps); + if (m_nativeSize != newNativeSize) { + m_nativeSize = newNativeSize; + emit nativeSizeChanged(); + } + gst_caps_unref(caps); + } + + // Start the surface + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle); + qDebug() << "m_nativeSize: " << m_nativeSize; + qDebug() << "format: " << format; + if (!m_surface->start(format)) { + qWarning() << Q_FUNC_INFO << ": failed to start the video surface " << format; + return; + } + } + + QGstreamerMirTextureBuffer *buffer = new QGstreamerMirTextureBuffer(m_textureId); + //qDebug() << "frameSize: " << m_surface->surfaceFormat().frameSize(); + QVideoFrame frame(buffer, m_surface->surfaceFormat().frameSize(), + m_surface->surfaceFormat().pixelFormat()); + + frame.setMetaData("TextureId", m_textureId); + + // Display the video frame on the surface: + m_surface->present(frame); +} + +GstPadProbeReturn QGstreamerMirTextureRenderer::padBufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer userData) +{ + Q_UNUSED(pad); + Q_UNUSED(info); + + QGstreamerMirTextureRenderer *control = reinterpret_cast(userData); + QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); + + return GST_PAD_PROBE_REMOVE; +} + +void QGstreamerMirTextureRenderer::stopRenderer() +{ + if (m_surface) + m_surface->stop(); +} + +QAbstractVideoSurface *QGstreamerMirTextureRenderer::surface() const +{ + return m_surface; +} + +void QGstreamerMirTextureRenderer::setSurface(QAbstractVideoSurface *surface) +{ + qDebug() << Q_FUNC_INFO; + + if (m_surface != surface) { + qDebug() << "Saving current QGLContext"; + m_context = const_cast(QGLContext::currentContext()); + + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + m_videoSink = 0; + + if (m_surface) { + disconnect(m_surface.data(), SIGNAL(supportedFormatsChanged()), + this, SLOT(handleFormatChange())); + } + + bool wasReady = isReady(); + + m_surface = surface; + + if (m_surface) { + connect(m_surface.data(), SIGNAL(supportedFormatsChanged()), + this, SLOT(handleFormatChange())); + } + + if (wasReady != isReady()) + emit readyChanged(isReady()); + + emit sinkChanged(); + } +} + +void QGstreamerMirTextureRenderer::setPlayerSession(const QGstreamerPlayerSession *playerSession) +{ + m_playerSession = const_cast(playerSession); +} + +void QGstreamerMirTextureRenderer::handleFormatChange() +{ + qDebug() << "Supported formats list has changed, reload video output"; + + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + m_videoSink = 0; + emit sinkChanged(); +} + +void QGstreamerMirTextureRenderer::updateNativeVideoSize() +{ + //qDebug() << Q_FUNC_INFO; + const QSize oldSize = m_nativeSize; + + if (m_videoSink) { + // Find video native size to update video widget size hint + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + GstCaps *caps = gst_pad_get_current_caps(pad); + + if (caps) { + m_nativeSize = QGstUtils::capsCorrectedResolution(caps); + gst_caps_unref(caps); + } + } else { + m_nativeSize = QSize(); + } + qDebug() << Q_FUNC_INFO << oldSize << m_nativeSize << m_videoSink; + + if (m_nativeSize != oldSize) + emit nativeSizeChanged(); +} + +void QGstreamerMirTextureRenderer::handleFocusWindowChanged(QWindow *window) +{ + qDebug() << Q_FUNC_INFO; + + QOpenGLContext *currContext = QOpenGLContext::currentContext(); + + QQuickWindow *w = dynamic_cast(window); + // If we don't have a GL context in the current thread, create one and share it + // with the render thread GL context + if (!currContext && !m_glContext) { + // This emulates the new QOffscreenWindow class with Qt5.1 + m_offscreenSurface = createOffscreenWindow(w->openglContext()->surface()->format()); + m_offscreenSurface->setParent(window); + + QOpenGLContext *shareContext = 0; + if (m_surface) + shareContext = qobject_cast(m_surface->property("GLContext").value()); + m_glContext = new QOpenGLContext; + m_glContext->setFormat(m_offscreenSurface->requestedFormat()); + + if (shareContext) + m_glContext->setShareContext(shareContext); + + if (!m_glContext->create()) + { + qWarning() << "Failed to create new shared context."; + return; + } + } + + if (m_glContext) + m_glContext->makeCurrent(m_offscreenSurface); + + if (m_textureId == 0) { + glGenTextures(1, &m_textureId); + qDebug() << "texture_id (handleFocusWindowChanged): " << m_textureId << endl; + g_object_set(G_OBJECT(m_videoSink), "texture-id", m_textureId, (char*)NULL); + } +} diff --git a/src/gsttools/qgstreamervideoprobecontrol.cpp b/src/gsttools/qgstreamervideoprobecontrol.cpp index a78a9dad0..71a402df0 100644 --- a/src/gsttools/qgstreamervideoprobecontrol.cpp +++ b/src/gsttools/qgstreamervideoprobecontrol.cpp @@ -32,7 +32,8 @@ ****************************************************************************/ #include "qgstreamervideoprobecontrol_p.h" -#include + +#include "qgstutils_p.h" #include QGstreamerVideoProbeControl::QGstreamerVideoProbeControl(QObject *parent) @@ -40,12 +41,10 @@ QGstreamerVideoProbeControl::QGstreamerVideoProbeControl(QObject *parent) , m_flushing(false) , m_frameProbed(false) { - } QGstreamerVideoProbeControl::~QGstreamerVideoProbeControl() { - } void QGstreamerVideoProbeControl::startFlushing() @@ -67,33 +66,49 @@ void QGstreamerVideoProbeControl::stopFlushing() m_flushing = false; } -void QGstreamerVideoProbeControl::bufferProbed(GstBuffer* buffer) +void QGstreamerVideoProbeControl::probeCaps(GstCaps *caps) { - if (m_flushing) - return; - - GstCaps* caps = gst_buffer_get_caps(buffer); - if (!caps) - return; +#if GST_CHECK_VERSION(1,0,0) + GstVideoInfo videoInfo; + QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &videoInfo); + QMutexLocker locker(&m_frameMutex); + m_videoInfo = videoInfo; +#else int bytesPerLine = 0; - QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine); - gst_caps_unref(caps); - if (!format.isValid() || !bytesPerLine) - return; + QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine); + + QMutexLocker locker(&m_frameMutex); + m_bytesPerLine = bytesPerLine; +#endif + m_format = format; +} + +bool QGstreamerVideoProbeControl::probeBuffer(GstBuffer *buffer) +{ + QMutexLocker locker(&m_frameMutex); + + if (m_flushing || !m_format.isValid()) + return true; - QVideoFrame frame = QVideoFrame(new QGstVideoBuffer(buffer, bytesPerLine), - format.frameSize(), format.pixelFormat()); + QVideoFrame frame( +#if GST_CHECK_VERSION(1,0,0) + new QGstVideoBuffer(buffer, m_videoInfo), +#else + new QGstVideoBuffer(buffer, m_bytesPerLine), +#endif + m_format.frameSize(), + m_format.pixelFormat()); - QVideoSurfaceGstSink::setFrameTimeStamps(&frame, buffer); + QGstUtils::setFrameTimeStamps(&frame, buffer); m_frameProbed = true; - { - QMutexLocker locker(&m_frameMutex); - m_pendingFrame = frame; + if (!m_pendingFrame.isValid()) QMetaObject::invokeMethod(this, "frameProbed", Qt::QueuedConnection); - } + m_pendingFrame = frame; + + return true; } void QGstreamerVideoProbeControl::frameProbed() @@ -104,6 +119,7 @@ void QGstreamerVideoProbeControl::frameProbed() if (!m_pendingFrame.isValid()) return; frame = m_pendingFrame; + m_pendingFrame = QVideoFrame(); } emit videoFrameProbed(frame); } diff --git a/src/gsttools/qgstreamervideorenderer.cpp b/src/gsttools/qgstreamervideorenderer.cpp index 2b66f761f..804dce9ae 100644 --- a/src/gsttools/qgstreamervideorenderer.cpp +++ b/src/gsttools/qgstreamervideorenderer.cpp @@ -35,8 +35,7 @@ #include #include #include - -#include +#include #include diff --git a/src/gsttools/qgstreamervideowidget.cpp b/src/gsttools/qgstreamervideowidget.cpp index aa2e2a303..1ae57a0e9 100644 --- a/src/gsttools/qgstreamervideowidget.cpp +++ b/src/gsttools/qgstreamervideowidget.cpp @@ -40,8 +40,13 @@ #include #include + +#if !GST_CHECK_VERSION(1,0,0) #include #include +#else +#include +#endif QT_BEGIN_NAMESPACE @@ -130,8 +135,6 @@ void QGstreamerVideoWidgetControl::createVideoWidget() m_videoSink = gst_element_factory_make ("ximagesink", NULL); qt_gst_object_ref_sink(GST_OBJECT (m_videoSink)); //Take ownership - - } GstElement *QGstreamerVideoWidgetControl::videoSink() @@ -169,9 +172,13 @@ bool QGstreamerVideoWidgetControl::processSyncMessage(const QGstreamerMessage &m { GstMessage* gm = message.rawMessage(); +#if !GST_CHECK_VERSION(1,0,0) if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && gst_structure_has_name(gm->structure, "prepare-xwindow-id")) { - +#else + if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && + gst_structure_has_name(gst_message_get_structure(gm), "prepare-window-handle")) { +#endif setOverlay(); QMetaObject::invokeMethod(this, "updateNativeVideoSize", Qt::QueuedConnection); return true; @@ -199,17 +206,24 @@ bool QGstreamerVideoWidgetControl::processBusMessage(const QGstreamerMessage &me void QGstreamerVideoWidgetControl::setOverlay() { +#if !GST_CHECK_VERSION(1,0,0) if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); } +#else + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), m_windowId); + } +#endif } void QGstreamerVideoWidgetControl::updateNativeVideoSize() { if (m_videoSink) { //find video native size to update video widget size hint - GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); - GstCaps *caps = gst_pad_get_negotiated_caps(pad); + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + GstCaps *caps = qt_gst_pad_get_current_caps(pad); + gst_object_unref(GST_OBJECT(pad)); if (caps) { @@ -225,8 +239,13 @@ void QGstreamerVideoWidgetControl::updateNativeVideoSize() void QGstreamerVideoWidgetControl::windowExposed() { +#if !GST_CHECK_VERSION(1,0,0) if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); +#else + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) + gst_video_overlay_expose(GST_VIDEO_OVERLAY(m_videoSink)); +#endif } QWidget *QGstreamerVideoWidgetControl::videoWidget() diff --git a/src/gsttools/qgstreamervideowindow.cpp b/src/gsttools/qgstreamervideowindow.cpp index a373dccf0..801134990 100644 --- a/src/gsttools/qgstreamervideowindow.cpp +++ b/src/gsttools/qgstreamervideowindow.cpp @@ -37,36 +37,49 @@ #include #include + +#if !GST_CHECK_VERSION(1,0,0) #include #include +#else +#include +#endif QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const char *elementName) : QVideoWindowControl(parent) + , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps) , m_videoSink(0) , m_windowId(0) , m_aspectRatioMode(Qt::KeepAspectRatio) , m_fullScreen(false) , m_colorKey(QColor::Invalid) { - if (elementName) + if (elementName) { m_videoSink = gst_element_factory_make(elementName, NULL); - else + } else { m_videoSink = gst_element_factory_make("xvimagesink", NULL); + } if (m_videoSink) { qt_gst_object_ref_sink(GST_OBJECT(m_videoSink)); //Take ownership - GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); - m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); + GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); + addProbeToPad(pad); gst_object_unref(GST_OBJECT(pad)); } + else + qDebug() << "No m_videoSink available!"; } QGstreamerVideoWindow::~QGstreamerVideoWindow() { - if (m_videoSink) + if (m_videoSink) { + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); gst_object_unref(GST_OBJECT(m_videoSink)); + } } WId QGstreamerVideoWindow::winId() const @@ -82,11 +95,15 @@ void QGstreamerVideoWindow::setWinId(WId id) WId oldId = m_windowId; m_windowId = id; - +#if GST_CHECK_VERSION(1,0,0) + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), m_windowId); + } +#else if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); } - +#endif if (!oldId) emit readyChanged(true); @@ -97,20 +114,26 @@ void QGstreamerVideoWindow::setWinId(WId id) bool QGstreamerVideoWindow::processSyncMessage(const QGstreamerMessage &message) { GstMessage* gm = message.rawMessage(); +#if GST_CHECK_VERSION(1,0,0) + const GstStructure *s = gst_message_get_structure(gm); + if ((GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && + gst_structure_has_name(s, "prepare-window-handle") && + m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { + + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink), m_windowId); + return true; + } +#else if ((GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) && gst_structure_has_name(gm->structure, "prepare-xwindow-id") && m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); - GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); - m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); - gst_object_unref(GST_OBJECT(pad)); - return true; } - +#endif return false; } @@ -122,7 +145,19 @@ QRect QGstreamerVideoWindow::displayRect() const void QGstreamerVideoWindow::setDisplayRect(const QRect &rect) { m_displayRect = rect; - +#if GST_CHECK_VERSION(1,0,0) + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { + if (m_displayRect.isEmpty()) + gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink), -1, -1, -1, -1); + else + gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height()); + repaint(); + } +#else if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { #if GST_VERSION_MICRO >= 29 if (m_displayRect.isEmpty()) @@ -136,6 +171,7 @@ void QGstreamerVideoWindow::setDisplayRect(const QRect &rect) repaint(); #endif } +#endif } Qt::AspectRatioMode QGstreamerVideoWindow::aspectRatioMode() const @@ -157,6 +193,16 @@ void QGstreamerVideoWindow::setAspectRatioMode(Qt::AspectRatioMode mode) void QGstreamerVideoWindow::repaint() { +#if GST_CHECK_VERSION(1,0,0) + if (m_videoSink && GST_IS_VIDEO_OVERLAY(m_videoSink)) { + //don't call gst_x_overlay_expose if the sink is in null state + GstState state = GST_STATE_NULL; + GstStateChangeReturn res = gst_element_get_state(m_videoSink, &state, NULL, 1000000); + if (res != GST_STATE_CHANGE_FAILURE && state != GST_STATE_NULL) { + gst_video_overlay_expose(GST_VIDEO_OVERLAY(m_videoSink)); + } + } +#else if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { //don't call gst_x_overlay_expose if the sink is in null state GstState state = GST_STATE_NULL; @@ -165,6 +211,7 @@ void QGstreamerVideoWindow::repaint() gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); } } +#endif } QColor QGstreamerVideoWindow::colorKey() const @@ -296,32 +343,22 @@ QSize QGstreamerVideoWindow::nativeSize() const return m_nativeSize; } -void QGstreamerVideoWindow::padBufferProbe(GstPad *pad, GstBuffer * /* buffer */, gpointer user_data) +void QGstreamerVideoWindow::probeCaps(GstCaps *caps) { - QGstreamerVideoWindow *control = reinterpret_cast(user_data); - QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); - gst_pad_remove_buffer_probe(pad, control->m_bufferProbeId); + QSize resolution = QGstUtils::capsCorrectedResolution(caps); + QMetaObject::invokeMethod( + this, + "updateNativeVideoSize", + Qt::QueuedConnection, + Q_ARG(QSize, resolution)); } -void QGstreamerVideoWindow::updateNativeVideoSize() +void QGstreamerVideoWindow::updateNativeVideoSize(const QSize &size) { - const QSize oldSize = m_nativeSize; - m_nativeSize = QSize(); - - if (m_videoSink) { - //find video native size to update video widget size hint - GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); - GstCaps *caps = gst_pad_get_negotiated_caps(pad); - gst_object_unref(GST_OBJECT(pad)); - - if (caps) { - m_nativeSize = QGstUtils::capsCorrectedResolution(caps); - gst_caps_unref(caps); - } - } - - if (m_nativeSize != oldSize) + if (m_nativeSize != size) { + m_nativeSize = size; emit nativeSizeChanged(); + } } GstElement *QGstreamerVideoWindow::videoSink() diff --git a/src/gsttools/qgstutils.cpp b/src/gsttools/qgstutils.cpp index 556fc03cc..65124c931 100644 --- a/src/gsttools/qgstutils.cpp +++ b/src/gsttools/qgstutils.cpp @@ -40,7 +40,14 @@ #include #include #include +#include #include +#include + +#include +#include + +template static int lengthOf(const T (&)[N]) { return N; } #ifdef USE_V4L # include @@ -82,15 +89,24 @@ static void addTagToMap(const GstTagList *list, map->insert(QByteArray(tag), g_value_get_boolean(&val)); break; case G_TYPE_CHAR: +#if GLIB_CHECK_VERSION(2,32,0) + map->insert(QByteArray(tag), g_value_get_schar(&val)); +#else map->insert(QByteArray(tag), g_value_get_char(&val)); +#endif break; case G_TYPE_DOUBLE: map->insert(QByteArray(tag), g_value_get_double(&val)); break; default: // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch +#if GST_CHECK_VERSION(1,0,0) + if (G_VALUE_TYPE(&val) == G_TYPE_DATE) { + const GDate *date = (const GDate *)g_value_get_boxed(&val); +#else if (G_VALUE_TYPE(&val) == GST_TYPE_DATE) { const GDate *date = gst_value_get_date(&val); +#endif if (g_date_valid(date)) { int year = g_date_get_year(date); int month = g_date_get_month(date); @@ -169,6 +185,42 @@ QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) return size; } + +#if GST_CHECK_VERSION(1,0,0) +namespace { + +struct AudioFormat +{ + GstAudioFormat format; + QAudioFormat::SampleType sampleType; + QAudioFormat::Endian byteOrder; + int sampleSize; +}; +static const AudioFormat qt_audioLookup[] = +{ + { GST_AUDIO_FORMAT_S8 , QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 8 }, + { GST_AUDIO_FORMAT_U8 , QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 8 }, + { GST_AUDIO_FORMAT_S16LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 16 }, + { GST_AUDIO_FORMAT_S16BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 16 }, + { GST_AUDIO_FORMAT_U16LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 16 }, + { GST_AUDIO_FORMAT_U16BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 16 }, + { GST_AUDIO_FORMAT_S32LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 32 }, + { GST_AUDIO_FORMAT_S32BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 32 }, + { GST_AUDIO_FORMAT_U32LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 32 }, + { GST_AUDIO_FORMAT_U32BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 32 }, + { GST_AUDIO_FORMAT_S24LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 24 }, + { GST_AUDIO_FORMAT_S24BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 24 }, + { GST_AUDIO_FORMAT_U24LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 24 }, + { GST_AUDIO_FORMAT_U24BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 24 }, + { GST_AUDIO_FORMAT_F32LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 32 }, + { GST_AUDIO_FORMAT_F32BE, QAudioFormat::Float , QAudioFormat::BigEndian , 32 }, + { GST_AUDIO_FORMAT_F64LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 64 }, + { GST_AUDIO_FORMAT_F64BE, QAudioFormat::Float , QAudioFormat::BigEndian , 64 } +}; + +} +#endif + /*! Returns audio format for caps. If caps doesn't have a valid audio format, an empty QAudioFormat is returned. @@ -176,9 +228,26 @@ QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps) { - const GstStructure *structure = gst_caps_get_structure(caps, 0); - QAudioFormat format; +#if GST_CHECK_VERSION(1,0,0) + GstAudioInfo info; + if (gst_audio_info_from_caps(&info, caps)) { + for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { + if (qt_audioLookup[i].format != info.finfo->format) + continue; + + format.setSampleType(qt_audioLookup[i].sampleType); + format.setByteOrder(qt_audioLookup[i].byteOrder); + format.setSampleSize(qt_audioLookup[i].sampleSize); + format.setSampleRate(info.rate); + format.setChannelCount(info.channels); + format.setCodec(QStringLiteral("audio/pcm")); + + return format; + } + } +#else + const GstStructure *structure = gst_caps_get_structure(caps, 0); if (qstrcmp(gst_structure_get_name(structure), "audio/x-raw-int") == 0) { @@ -249,16 +318,28 @@ QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps) } else { return QAudioFormat(); } - +#endif return format; } +#if GST_CHECK_VERSION(1,0,0) +/*! + Returns audio format for a sample. + If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. +*/ +QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample) +{ + GstCaps* caps = gst_sample_get_caps(sample); + if (!caps) + return QAudioFormat(); + return QGstUtils::audioFormatForCaps(caps); +} +#else /*! Returns audio format for a buffer. If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. */ - QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer) { GstCaps* caps = gst_buffer_get_caps(buffer); @@ -269,7 +350,7 @@ QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer) gst_caps_unref(caps); return format; } - +#endif /*! Builds GstCaps for an audio format. @@ -277,8 +358,32 @@ QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer) Caller must unref GstCaps. */ -GstCaps *QGstUtils::capsForAudioFormat(QAudioFormat format) +GstCaps *QGstUtils::capsForAudioFormat(const QAudioFormat &format) { + if (!format.isValid()) + return 0; + +#if GST_CHECK_VERSION(1,0,0) + const QAudioFormat::SampleType sampleType = format.sampleType(); + const QAudioFormat::Endian byteOrder = format.byteOrder(); + const int sampleSize = format.sampleSize(); + + for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { + if (qt_audioLookup[i].sampleType != sampleType + || qt_audioLookup[i].byteOrder != byteOrder + || qt_audioLookup[i].sampleSize != sampleSize) { + continue; + } + + return gst_caps_new_simple( + "audio/x-raw", + "format" , G_TYPE_STRING, gst_audio_format_to_string(qt_audioLookup[i].format), + "rate" , G_TYPE_INT , format.sampleRate(), + "channels", G_TYPE_INT , format.channelCount(), + NULL); + } + return 0; +#else GstStructure *structure = 0; if (format.isValid()) { @@ -313,6 +418,7 @@ GstCaps *QGstUtils::capsForAudioFormat(QAudioFormat format) } return caps; +#endif } void QGstUtils::initializeGst() @@ -576,10 +682,629 @@ QByteArray QGstUtils::cameraDriver(const QString &device, GstElementFactory *fac return QByteArray(); } +QSet QGstUtils::supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)) +{ + QSet supportedMimeTypes; + + //enumerate supported mime types + gst_init(NULL, NULL); + +#if GST_CHECK_VERSION(1,0,0) + GstRegistry *registry = gst_registry_get(); + GList *orig_plugins = gst_registry_get_plugin_list(registry); +#else + GstRegistry *registry = gst_registry_get_default(); + GList *orig_plugins = gst_default_registry_get_plugin_list (); +#endif + for (GList *plugins = orig_plugins; plugins; plugins = g_list_next(plugins)) { + GstPlugin *plugin = (GstPlugin *) (plugins->data); +#if GST_CHECK_VERSION(1,0,0) + if (GST_OBJECT_FLAG_IS_SET(GST_OBJECT(plugin), GST_PLUGIN_FLAG_BLACKLISTED)) + continue; +#else + if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED + continue; +#endif + + GList *orig_features = gst_registry_get_feature_list_by_plugin( + registry, gst_plugin_get_name(plugin)); + for (GList *features = orig_features; features; features = g_list_next(features)) { + if (G_UNLIKELY(features->data == NULL)) + continue; + + GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); + GstElementFactory *factory; + + if (GST_IS_TYPE_FIND_FACTORY(feature)) { + QString name(gst_plugin_feature_get_name(feature)); + if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type + supportedMimeTypes.insert(name.toLower()); + continue; + } else if (!GST_IS_ELEMENT_FACTORY (feature) + || !(factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)))) { + continue; + } else if (!isValidFactory(factory)) { + // Do nothing + } else for (const GList *pads = gst_element_factory_get_static_pad_templates(factory); + pads; + pads = g_list_next(pads)) { + GstStaticPadTemplate *padtemplate = static_cast(pads->data); + + if (padtemplate->direction == GST_PAD_SINK && padtemplate->static_caps.string) { + GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); + if (gst_caps_is_any(caps) || gst_caps_is_empty(caps)) { + } else for (guint i = 0; i < gst_caps_get_size(caps); i++) { + GstStructure *structure = gst_caps_get_structure(caps, i); + QString nameLowcase = QString(gst_structure_get_name(structure)).toLower(); + + supportedMimeTypes.insert(nameLowcase); + if (nameLowcase.contains("mpeg")) { + //Because mpeg version number is only included in the detail + //description, it is necessary to manually extract this information + //in order to match the mime type of mpeg4. + const GValue *value = gst_structure_get_value(structure, "mpegversion"); + if (value) { + gchar *str = gst_value_serialize(value); + QString versions(str); + QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); + foreach (const QString &e, elements) + supportedMimeTypes.insert(nameLowcase + e); + g_free(str); + } + } + } + } + } + gst_object_unref(factory); + } + gst_plugin_feature_list_free(orig_features); + } + gst_plugin_list_free (orig_plugins); + +#if defined QT_SUPPORTEDMIMETYPES_DEBUG + QStringList list = supportedMimeTypes.toList(); + list.sort(); + if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { + foreach (const QString &type, list) + qDebug() << type; + } +#endif + return supportedMimeTypes; +} + +namespace { + +struct ColorFormat { QImage::Format imageFormat; GstVideoFormat gstFormat; }; +static const ColorFormat qt_colorLookup[] = +{ + { QImage::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx }, + { QImage::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA }, + { QImage::Format_RGB888 , GST_VIDEO_FORMAT_RGB }, + { QImage::Format_RGB16 , GST_VIDEO_FORMAT_RGB16 } +}; + +} + +#if GST_CHECK_VERSION(1,0,0) +QImage QGstUtils::bufferToImage(GstBuffer *buffer, const GstVideoInfo &videoInfo) +#else +QImage QGstUtils::bufferToImage(GstBuffer *buffer) +#endif +{ + QImage img; + +#if GST_CHECK_VERSION(1,0,0) + GstVideoInfo info = videoInfo; + GstVideoFrame frame; + if (!gst_video_frame_map(&frame, &info, buffer, GST_MAP_READ)) + return img; +#else + GstCaps *caps = gst_buffer_get_caps(buffer); + if (!caps) + return img; + + GstStructure *structure = gst_caps_get_structure (caps, 0); + gint width = 0; + gint height = 0; + + if (!structure + || !gst_structure_get_int(structure, "width", &width) + || !gst_structure_get_int(structure, "height", &height) + || width <= 0 + || height <= 0) { + gst_caps_unref(caps); + return img; + } + gst_caps_unref(caps); +#endif + +#if GST_CHECK_VERSION(1,0,0) + if (videoInfo.finfo->format == GST_VIDEO_FORMAT_I420) { + const int width = videoInfo.width; + const int height = videoInfo.height; + + const int stride[] = { frame.info.stride[0], frame.info.stride[1], frame.info.stride[2] }; + const uchar *data[] = { + static_cast(frame.data[0]), + static_cast(frame.data[1]), + static_cast(frame.data[2]) + }; +#else + if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { + const int stride[] = { width, width / 2, width / 2 }; + const uchar *data[] = { + (const uchar *)buffer->data, + (const uchar *)buffer->data + width * height, + (const uchar *)buffer->data + width * height * 5 / 4 + }; +#endif + img = QImage(width/2, height/2, QImage::Format_RGB32); + + for (int y=0; yformat) + continue; + + const QImage image( + static_cast(frame.data[0]), + videoInfo.width, + videoInfo.height, + frame.info.stride[0], + qt_colorLookup[i].imageFormat); + img = image; + img.detach(); + + break; + } + + gst_video_frame_unmap(&frame); +#else + } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { + QImage::Format format = QImage::Format_Invalid; + int bpp = 0; + gst_structure_get_int(structure, "bpp", &bpp); + + if (bpp == 24) + format = QImage::Format_RGB888; + else if (bpp == 32) + format = QImage::Format_RGB32; + + if (format != QImage::Format_Invalid) { + img = QImage((const uchar *)buffer->data, + width, + height, + format); + img.bits(); //detach + } + } +#endif + return img; +} + + +namespace { + +#if GST_CHECK_VERSION(1,0,0) + +struct VideoFormat +{ + QVideoFrame::PixelFormat pixelFormat; + GstVideoFormat gstFormat; +}; + +static const VideoFormat qt_videoFormatLookup[] = +{ + { QVideoFrame::Format_YUV420P, GST_VIDEO_FORMAT_I420 }, + { QVideoFrame::Format_YV12 , GST_VIDEO_FORMAT_YV12 }, + { QVideoFrame::Format_UYVY , GST_VIDEO_FORMAT_UYVY }, + { QVideoFrame::Format_YUYV , GST_VIDEO_FORMAT_YUY2 }, + { QVideoFrame::Format_NV12 , GST_VIDEO_FORMAT_NV12 }, + { QVideoFrame::Format_NV21 , GST_VIDEO_FORMAT_NV21 }, + { QVideoFrame::Format_AYUV444, GST_VIDEO_FORMAT_AYUV }, +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_BGRx }, + { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_RGBx }, + { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_BGRA }, + { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_ARGB }, +#else + { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_xRGB }, + { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_xBGR }, + { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_ARGB }, + { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_BGRA }, +#endif + { QVideoFrame::Format_RGB24 , GST_VIDEO_FORMAT_RGB }, + { QVideoFrame::Format_BGR24 , GST_VIDEO_FORMAT_BGR }, + { QVideoFrame::Format_RGB565, GST_VIDEO_FORMAT_RGB16 } +}; + +static int indexOfVideoFormat(QVideoFrame::PixelFormat format) +{ + for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) + if (qt_videoFormatLookup[i].pixelFormat == format) + return i; + + return -1; +} + +static int indexOfVideoFormat(GstVideoFormat format) +{ + for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) + if (qt_videoFormatLookup[i].gstFormat == format) + return i; + + return -1; +} + +#else + +struct YuvFormat +{ + QVideoFrame::PixelFormat pixelFormat; + guint32 fourcc; + int bitsPerPixel; +}; + +static const YuvFormat qt_yuvColorLookup[] = +{ + { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, + { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, + { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, + { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, + { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, + { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 }, + { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 } +}; + +static int indexOfYuvColor(QVideoFrame::PixelFormat format) +{ + const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].pixelFormat == format) + return i; + + return -1; +} + +static int indexOfYuvColor(guint32 fourcc) +{ + const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].fourcc == fourcc) + return i; + + return -1; +} + +struct RgbFormat +{ + QVideoFrame::PixelFormat pixelFormat; + int bitsPerPixel; + int depth; + int endianness; + int red; + int green; + int blue; + int alpha; +}; + +static const RgbFormat qt_rgbColorLookup[] = +{ + { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x00000000 }, + { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_BGR32 , 32, 24, 4321, int(0xFF000000), 0x00FF0000, 0x0000FF00, 0x00000000 }, + { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, + { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x000000FF }, + { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, int(0xFF000000) }, + { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, + { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } +}; + +static int indexOfRgbColor( + int bits, int depth, int endianness, int red, int green, int blue, int alpha) +{ + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].bitsPerPixel == bits + && qt_rgbColorLookup[i].depth == depth + && qt_rgbColorLookup[i].endianness == endianness + && qt_rgbColorLookup[i].red == red + && qt_rgbColorLookup[i].green == green + && qt_rgbColorLookup[i].blue == blue + && qt_rgbColorLookup[i].alpha == alpha) { + return i; + } + } + return -1; +} +#endif + +} + +#if GST_CHECK_VERSION(1,0,0) + +QVideoSurfaceFormat QGstUtils::formatForCaps( + GstCaps *caps, GstVideoInfo *info, QAbstractVideoBuffer::HandleType handleType) +{ + if (gst_video_info_from_caps(info, caps)) { + int index = indexOfVideoFormat(info->finfo->format); + + if (index != -1) { + QVideoSurfaceFormat format( + QSize(info->width, info->height), + qt_videoFormatLookup[index].pixelFormat, + handleType); + + if (info->fps_d > 0) + format.setFrameRate(qreal(info->fps_d) / info->fps_n); + + if (info->par_d > 0) + format.setPixelAspectRatio(info->par_n, info->par_d); + + return format; + } + } + return QVideoSurfaceFormat(); +} + +#else + +QVideoSurfaceFormat QGstUtils::formatForCaps( + GstCaps *caps, int *bytesPerLine, QAbstractVideoBuffer::HandleType handleType) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + + QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; + int bitsPerPixel = 0; + + QSize size; + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + + if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { + guint32 fourcc = 0; + gst_structure_get_fourcc(structure, "format", &fourcc); + + int index = indexOfYuvColor(fourcc); + if (index != -1) { + pixelFormat = qt_yuvColorLookup[index].pixelFormat; + bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel; + } + } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { + int depth = 0; + int endianness = 0; + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + + gst_structure_get_int(structure, "bpp", &bitsPerPixel); + gst_structure_get_int(structure, "depth", &depth); + gst_structure_get_int(structure, "endianness", &endianness); + gst_structure_get_int(structure, "red_mask", &red); + gst_structure_get_int(structure, "green_mask", &green); + gst_structure_get_int(structure, "blue_mask", &blue); + gst_structure_get_int(structure, "alpha_mask", &alpha); + + int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); + + if (index != -1) + pixelFormat = qt_rgbColorLookup[index].pixelFormat; + } + + if (pixelFormat != QVideoFrame::Format_Invalid) { + QVideoSurfaceFormat format(size, pixelFormat, handleType); + + QPair rate; + gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second); + + if (rate.second) + format.setFrameRate(qreal(rate.first)/rate.second); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (gst_structure_get_fraction( + structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + format.setPixelAspectRatio(aspectNum, aspectDenum); + } + + if (bytesPerLine) + *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; + + return format; + } + return QVideoSurfaceFormat(); +} + +#endif + +GstCaps *QGstUtils::capsForFormats(const QList &formats) +{ + GstCaps *caps = gst_caps_new_empty(); + +#if GST_CHECK_VERSION(1,0,0) + foreach (QVideoFrame::PixelFormat format, formats) { + int index = indexOfVideoFormat(format); + + if (index != -1) { + gst_caps_append_structure(caps, gst_structure_new( + "video/x-raw", + "format" , G_TYPE_STRING, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat), + NULL)); + } + } +#else + foreach (QVideoFrame::PixelFormat format, formats) { + int index = indexOfYuvColor(format); + + if (index != -1) { + gst_caps_append_structure(caps, gst_structure_new( + "video/x-raw-yuv", + "format", GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, + NULL)); + continue; + } + + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].pixelFormat == format) { + GstStructure *structure = gst_structure_new( + "video/x-raw-rgb", + "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, + "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, + "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, + "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, + "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, + "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, + NULL); + + if (qt_rgbColorLookup[i].alpha != 0) { + gst_structure_set( + structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL); + } + gst_caps_append_structure(caps, structure); + } + } + } +#endif + + gst_caps_set_simple( + caps, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + NULL); + + return caps; +} + +void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) +{ + // GStreamer uses nanoseconds, Qt uses microseconds + qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); + if (startTime >= 0) { + frame->setStartTime(startTime/G_GINT64_CONSTANT (1000)); + + qint64 duration = GST_BUFFER_DURATION(buffer); + if (duration >= 0) + frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); + } +} + +void QGstUtils::setMetaData(GstElement *element, const QMap &data) +{ + if (!GST_IS_TAG_SETTER(element)) + return; + + gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); + + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + const QString tagName = it.key(); + const QVariant tagValue = it.value(); + + switch (tagValue.type()) { + case QVariant::String: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + tagValue.toString().toUtf8().constData(), + NULL); + break; + case QVariant::Int: + case QVariant::LongLong: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + tagValue.toInt(), + NULL); + break; + case QVariant::Double: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + tagValue.toDouble(), + NULL); + break; + case QVariant::DateTime: { + QDateTime date = tagValue.toDateTime().toLocalTime(); + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE, + tagName.toUtf8().constData(), + gst_date_time_new_local_time( + date.date().year(), date.date().month(), date.date().day(), + date.time().hour(), date.time().minute(), date.time().second()), + NULL); + break; + } + default: + break; + } + } +} + +void QGstUtils::setMetaData(GstBin *bin, const QMap &data) +{ + GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER); +#if GST_CHECK_VERSION(1,0,0) + GValue item = G_VALUE_INIT; + while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) { + GstElement * const element = GST_ELEMENT(g_value_get_object(&item)); +#else + GstElement *element = 0; + while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { +#endif + setMetaData(element, data); + } + gst_iterator_free(elements); +} + + +GstCaps *QGstUtils::videoFilterCaps() +{ + static GstStaticCaps staticCaps = GST_STATIC_CAPS( +#if GST_CHECK_VERSION(1,2,0) + "video/x-raw(ANY);" +#elif GST_CHECK_VERSION(1,0,0) + "video/x-raw;" +#else + "video/x-raw-yuv;" + "video/x-raw-rgb;" + "video/x-raw-data;" + "video/x-android-buffer;" +#endif + "image/jpeg;" + "video/x-h264"); + + return gst_caps_make_writable(gst_static_caps_get(&staticCaps)); +} void qt_gst_object_ref_sink(gpointer object) { -#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 24) +#if GST_CHECK_VERSION(0,10,24) gst_object_ref_sink(object); #else g_return_if_fail (GST_IS_OBJECT(object)); @@ -595,4 +1320,50 @@ void qt_gst_object_ref_sink(gpointer object) #endif } +GstCaps *qt_gst_pad_get_current_caps(GstPad *pad) +{ +#if GST_CHECK_VERSION(1,0,0) + return gst_pad_get_current_caps(pad); +#else + return gst_pad_get_negotiated_caps(pad); +#endif +} + +GstStructure *qt_gst_structure_new_empty(const char *name) +{ +#if GST_CHECK_VERSION(1,0,0) + return gst_structure_new_empty(name); +#else + return gst_structure_new(name, NULL); +#endif +} + +gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur) +{ +#if GST_CHECK_VERSION(1,0,0) + return gst_element_query_position(element, format, cur); +#else + return gst_element_query_position(element, &format, cur); +#endif +} + +gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur) +{ +#if GST_CHECK_VERSION(1,0,0) + return gst_element_query_duration(element, format, cur); +#else + return gst_element_query_duration(element, &format, cur); +#endif +} + +QDebug operator <<(QDebug debug, GstCaps *caps) +{ + if (caps) { + gchar *string = gst_caps_to_string(caps); + debug = debug << string; + g_free(string); + } + return debug; +} + QT_END_NAMESPACE diff --git a/src/gsttools/qgstvideobuffer.cpp b/src/gsttools/qgstvideobuffer.cpp index 18702ec76..1ce07ca12 100644 --- a/src/gsttools/qgstvideobuffer.cpp +++ b/src/gsttools/qgstvideobuffer.cpp @@ -35,21 +35,35 @@ QT_BEGIN_NAMESPACE +#if GST_CHECK_VERSION(1,0,0) +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info) + : QAbstractPlanarVideoBuffer(NoHandle) + , m_videoInfo(info) +#else QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine) : QAbstractVideoBuffer(NoHandle) - , m_buffer(buffer) , m_bytesPerLine(bytesPerLine) +#endif + , m_buffer(buffer) , m_mode(NotMapped) { gst_buffer_ref(m_buffer); } +#if GST_CHECK_VERSION(1,0,0) +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, + QGstVideoBuffer::HandleType handleType, + const QVariant &handle) + : QAbstractPlanarVideoBuffer(handleType) + , m_videoInfo(info) +#else QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, QGstVideoBuffer::HandleType handleType, const QVariant &handle) : QAbstractVideoBuffer(handleType) - , m_buffer(buffer) , m_bytesPerLine(bytesPerLine) +#endif + , m_buffer(buffer) , m_mode(NotMapped) , m_handle(handle) { @@ -58,6 +72,8 @@ QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, QGstVideoBuffer::~QGstVideoBuffer() { + unmap(); + gst_buffer_unref(m_buffer); } @@ -67,12 +83,49 @@ QAbstractVideoBuffer::MapMode QGstVideoBuffer::mapMode() const return m_mode; } +#if GST_CHECK_VERSION(1,0,0) + +int QGstVideoBuffer::map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) +{ + const GstMapFlags flags = GstMapFlags(((mode & ReadOnly) ? GST_MAP_READ : 0) + | ((mode & WriteOnly) ? GST_MAP_WRITE : 0)); + + if (mode == NotMapped || m_mode != NotMapped) { + return 0; + } else if (m_videoInfo.finfo->n_planes == 0) { // Encoded + if (gst_buffer_map(m_buffer, &m_frame.map[0], flags)) { + if (numBytes) + *numBytes = m_frame.map[0].size; + bytesPerLine[0] = -1; + data[0] = static_cast(m_frame.map[0].data); + + m_mode = mode; + + return 1; + } + } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, flags)) { + if (numBytes) + *numBytes = m_frame.info.size; + + for (guint i = 0; i < m_frame.info.finfo->n_planes; ++i) { + bytesPerLine[i] = m_frame.info.stride[i]; + data[i] = static_cast(m_frame.data[i]); + } + + m_mode = mode; + + return m_frame.info.finfo->n_planes; + } + return 0; +} + +#else + uchar *QGstVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) { if (mode != NotMapped && m_mode == NotMapped) { if (numBytes) *numBytes = m_buffer->size; - if (bytesPerLine) *bytesPerLine = m_bytesPerLine; @@ -83,8 +136,19 @@ uchar *QGstVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) return 0; } } + +#endif + void QGstVideoBuffer::unmap() { +#if GST_CHECK_VERSION(1,0,0) + if (m_mode != NotMapped) { + if (m_videoInfo.finfo->n_planes == 0) + gst_buffer_unmap(m_buffer, &m_frame.map[0]); + else + gst_video_frame_unmap(&m_frame); + } +#endif m_mode = NotMapped; } diff --git a/src/gsttools/qgstvideorendererplugin.cpp b/src/gsttools/qgstvideorendererplugin.cpp new file mode 100644 index 000000000..5eda85af9 --- /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 000000000..1102c2a1c --- /dev/null +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "qgstvideobuffer_p.h" + +#include "qgstvideorenderersink_p.h" + +#include + +#include "qgstutils_p.h" + +//#define DEBUG_VIDEO_SURFACE_SINK + +QT_BEGIN_NAMESPACE + +QGstDefaultVideoRenderer::QGstDefaultVideoRenderer() + : m_flushed(true) +{ +} + +QGstDefaultVideoRenderer::~QGstDefaultVideoRenderer() +{ +} + +GstCaps *QGstDefaultVideoRenderer::getCaps(QAbstractVideoSurface *surface) +{ + return QGstUtils::capsForFormats(surface->supportedPixelFormats()); +} + +bool QGstDefaultVideoRenderer::start(QAbstractVideoSurface *surface, GstCaps *caps) +{ + m_flushed = true; + m_format = QGstUtils::formatForCaps(caps, &m_videoInfo); + + return m_format.isValid() && surface->start(m_format); +} + +void QGstDefaultVideoRenderer::stop(QAbstractVideoSurface *surface) +{ + m_flushed = true; + if (surface) + surface->stop(); +} + +bool QGstDefaultVideoRenderer::present(QAbstractVideoSurface *surface, GstBuffer *buffer) +{ + m_flushed = false; + QVideoFrame frame( + new QGstVideoBuffer(buffer, m_videoInfo), + m_format.frameSize(), + m_format.pixelFormat()); + QGstUtils::setFrameTimeStamps(&frame, buffer); + + return surface->present(frame); +} + +void QGstDefaultVideoRenderer::flush(QAbstractVideoSurface *surface) +{ + if (surface && !m_flushed) + surface->present(QVideoFrame()); + m_flushed = true; +} + +bool QGstDefaultVideoRenderer::proposeAllocation(GstQuery *) +{ + return true; +} + +Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, rendererLoader, + (QGstVideoRendererInterface_iid, QLatin1String("video/gstvideorenderer"), Qt::CaseInsensitive)) + +QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface) + : m_surface(surface) + , m_renderer(0) + , m_activeRenderer(0) + , m_surfaceCaps(0) + , m_startCaps(0) + , m_lastBuffer(0) + , m_notified(false) + , m_stop(false) + , m_render(false) + , m_flush(false) +{ + foreach (QObject *instance, rendererLoader()->instances(QGstVideoRendererPluginKey)) { + QGstVideoRendererInterface* plugin = qobject_cast(instance); + if (QGstVideoRenderer *renderer = plugin ? plugin->createRenderer() : 0) + m_renderers.append(renderer); + } + + m_renderers.append(new QGstDefaultVideoRenderer); + updateSupportedFormats(); + connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats())); +} + +QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate() +{ + qDeleteAll(m_renderers); + + if (m_surfaceCaps) + gst_caps_unref(m_surfaceCaps); +} + +GstCaps *QVideoSurfaceGstDelegate::caps() +{ + QMutexLocker locker(&m_mutex); + + gst_caps_ref(m_surfaceCaps); + + return m_surfaceCaps; +} + +bool QVideoSurfaceGstDelegate::start(GstCaps *caps) +{ + QMutexLocker locker(&m_mutex); + + if (m_activeRenderer) { + m_flush = true; + m_stop = true; + } + + m_render = false; + + if (m_lastBuffer) { + gst_buffer_unref(m_lastBuffer); + m_lastBuffer = 0; + } + + if (m_startCaps) + gst_caps_unref(m_startCaps); + m_startCaps = caps; + gst_caps_ref(m_startCaps); + + /* + Waiting for start() to be invoked in the main thread may block + if gstreamer blocks the main thread until this call is finished. + This situation is rare and usually caused by setState(Null) + while pipeline is being prerolled. + + The proper solution to this involves controlling gstreamer pipeline from + other thread than video surface. + + Currently start() fails if wait() timed out. + */ + if (!waitForAsyncEvent(&locker, &m_setupCondition, 1000) && m_startCaps) { + qWarning() << "Failed to start video surface due to main thread blocked."; + gst_caps_unref(m_startCaps); + m_startCaps = 0; + } + + return m_activeRenderer != 0; +} + +void QVideoSurfaceGstDelegate::stop() +{ + QMutexLocker locker(&m_mutex); + + if (!m_activeRenderer) + return; + + m_flush = true; + m_stop = true; + + if (m_startCaps) { + gst_caps_unref(m_startCaps); + m_startCaps = 0; + } + + if (m_lastBuffer) { + gst_buffer_unref(m_lastBuffer); + m_lastBuffer = 0; + } + + waitForAsyncEvent(&locker, &m_setupCondition, 500); +} + +bool QVideoSurfaceGstDelegate::proposeAllocation(GstQuery *query) +{ + QMutexLocker locker(&m_mutex); + + if (QGstVideoRenderer *pool = m_activeRenderer) { + locker.unlock(); + + return pool->proposeAllocation(query); + } else { + return false; + } +} + +void QVideoSurfaceGstDelegate::flush() +{ + QMutexLocker locker(&m_mutex); + + m_flush = true; + m_render = false; + + if (m_lastBuffer) { + gst_buffer_unref(m_lastBuffer); + m_lastBuffer = 0; + } + + notify(); +} + +GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer, bool show) +{ + QMutexLocker locker(&m_mutex); + + if (m_lastBuffer) + gst_buffer_unref(m_lastBuffer); + m_lastBuffer = buffer; + gst_buffer_ref(m_lastBuffer); + + if (show) { + m_render = true; + + return waitForAsyncEvent(&locker, &m_renderCondition, 300) + ? m_renderReturn + : GST_FLOW_ERROR; + } else { + return GST_FLOW_OK; + } +} + +void QVideoSurfaceGstDelegate::handleShowPrerollChange(GObject *object, GParamSpec *, gpointer d) +{ + QVideoSurfaceGstDelegate * const delegate = static_cast(d); + + gboolean showPreroll = true; // "show-preroll-frame" property is true by default + g_object_get(object, "show-preroll-frame", &showPreroll, NULL); + + GstState state = GST_STATE_NULL; + GstState pendingState = GST_STATE_NULL; + gst_element_get_state(GST_ELEMENT(object), &state, &pendingState, 0); + + const bool paused + = (pendingState == GST_STATE_VOID_PENDING && state == GST_STATE_PAUSED) + || pendingState == GST_STATE_PAUSED; + + if (paused) { + QMutexLocker locker(&delegate->m_mutex); + + if (!showPreroll && delegate->m_lastBuffer) { + delegate->m_render = false; + delegate->m_flush = true; + delegate->notify(); + } else if (delegate->m_lastBuffer) { + delegate->m_render = true; + delegate->notify(); + } + } +} + +bool QVideoSurfaceGstDelegate::event(QEvent *event) +{ + if (event->type() == QEvent::UpdateRequest) { + QMutexLocker locker(&m_mutex); + + if (m_notified) { + while (handleEvent(&locker)) {} + m_notified = false; + } + return true; + } else { + return QObject::event(event); + } +} + +bool QVideoSurfaceGstDelegate::handleEvent(QMutexLocker *locker) +{ + if (m_flush) { + m_flush = false; + if (m_activeRenderer) { + locker->unlock(); + + m_activeRenderer->flush(m_surface); + } + } else if (m_stop) { + m_stop = false; + + if (QGstVideoRenderer * const activePool = m_activeRenderer) { + m_activeRenderer = 0; + locker->unlock(); + + activePool->stop(m_surface); + + locker->relock(); + } + } else if (m_startCaps) { + Q_ASSERT(!m_activeRenderer); + + GstCaps * const startCaps = m_startCaps; + m_startCaps = 0; + + if (m_renderer && m_surface) { + locker->unlock(); + + const bool started = m_renderer->start(m_surface, startCaps); + + locker->relock(); + + m_activeRenderer = started + ? m_renderer + : 0; + } else if (QGstVideoRenderer * const activePool = m_activeRenderer) { + m_activeRenderer = 0; + locker->unlock(); + + activePool->stop(m_surface); + + locker->relock(); + } + + gst_caps_unref(startCaps); + } else if (m_render) { + m_render = false; + + if (m_activeRenderer && m_surface && m_lastBuffer) { + GstBuffer *buffer = m_lastBuffer; + gst_buffer_ref(buffer); + + locker->unlock(); + + const bool rendered = m_activeRenderer->present(m_surface, buffer); + + gst_buffer_unref(buffer); + + locker->relock(); + + m_renderReturn = rendered + ? GST_FLOW_OK + : GST_FLOW_ERROR; + + m_renderCondition.wakeAll(); + } else { + m_renderReturn = GST_FLOW_ERROR; + m_renderCondition.wakeAll(); + } + } else { + m_setupCondition.wakeAll(); + + return false; + } + return true; +} + +void QVideoSurfaceGstDelegate::notify() +{ + if (!m_notified) { + m_notified = true; + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } +} + +bool QVideoSurfaceGstDelegate::waitForAsyncEvent( + QMutexLocker *locker, QWaitCondition *condition, unsigned long time) +{ + if (QThread::currentThread() == thread()) { + while (handleEvent(locker)) {} + m_notified = false; + + return true; + } else { + notify(); + + return condition->wait(&m_mutex, time); + } +} + +void QVideoSurfaceGstDelegate::updateSupportedFormats() +{ + if (m_surfaceCaps) { + gst_caps_unref(m_surfaceCaps); + m_surfaceCaps = 0; + } + + foreach (QGstVideoRenderer *pool, m_renderers) { + if (GstCaps *caps = pool->getCaps(m_surface)) { + if (gst_caps_is_empty(caps)) { + gst_caps_unref(caps); + continue; + } + + if (m_surfaceCaps) + gst_caps_unref(m_surfaceCaps); + + m_renderer = pool; + m_surfaceCaps = caps; + break; + } else { + gst_caps_unref(caps); + } + } +} + +static GstVideoSinkClass *sink_parent_class; + +#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast(s)) + +QGstVideoRendererSink *QGstVideoRendererSink::createSink(QAbstractVideoSurface *surface) +{ + QGstVideoRendererSink *sink = reinterpret_cast( + g_object_new(QGstVideoRendererSink::get_type(), 0)); + + sink->delegate = new QVideoSurfaceGstDelegate(surface); + + g_signal_connect( + G_OBJECT(sink), + "notify::show-preroll-frame", + G_CALLBACK(QVideoSurfaceGstDelegate::handleShowPrerollChange), + sink->delegate); + + return sink; +} + +GType QGstVideoRendererSink::get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = + { + sizeof(QGstVideoRendererSinkClass), // class_size + base_init, // base_init + NULL, // base_finalize + class_init, // class_init + NULL, // class_finalize + NULL, // class_data + sizeof(QGstVideoRendererSink), // instance_size + 0, // n_preallocs + instance_init, // instance_init + 0 // value_table + }; + + type = g_type_register_static( + GST_TYPE_VIDEO_SINK, "QGstVideoRendererSink", &info, GTypeFlags(0)); + } + + return type; +} + +void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + + sink_parent_class = reinterpret_cast(g_type_class_peek_parent(g_class)); + + GstBaseSinkClass *base_sink_class = reinterpret_cast(g_class); + base_sink_class->get_caps = QGstVideoRendererSink::get_caps; + base_sink_class->set_caps = QGstVideoRendererSink::set_caps; + base_sink_class->propose_allocation = QGstVideoRendererSink::propose_allocation; + base_sink_class->preroll = QGstVideoRendererSink::preroll; + base_sink_class->render = QGstVideoRendererSink::render; + + GstElementClass *element_class = reinterpret_cast(g_class); + element_class->change_state = QGstVideoRendererSink::change_state; + + GObjectClass *object_class = reinterpret_cast(g_class); + object_class->finalize = QGstVideoRendererSink::finalize; +} + +void QGstVideoRendererSink::base_init(gpointer g_class) +{ + static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( + "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( + "video/x-raw, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]")); + + gst_element_class_add_pad_template( + GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); +} + +void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class) +{ + VO_SINK(instance); + + Q_UNUSED(g_class); + + sink->delegate = 0; +} + +void QGstVideoRendererSink::finalize(GObject *object) +{ + VO_SINK(object); + + delete sink->delegate; + + // Chain up + G_OBJECT_CLASS(sink_parent_class)->finalize(object); +} + +GstStateChangeReturn QGstVideoRendererSink::change_state( + GstElement *element, GstStateChange transition) +{ + Q_UNUSED(element); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state( + element, transition); +} + +GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter) +{ + VO_SINK(base); + + GstCaps *caps = sink->delegate->caps(); + GstCaps *unfiltered = caps; + if (filter) { + caps = gst_caps_intersect(unfiltered, filter); + gst_caps_unref(unfiltered); + } + + return caps; +} + +gboolean QGstVideoRendererSink::set_caps(GstBaseSink *base, GstCaps *caps) +{ + VO_SINK(base); + +#ifdef DEBUG_VIDEO_SURFACE_SINK + qDebug() << "set_caps:"; + qDebug() << caps; +#endif + + if (!caps) { + sink->delegate->stop(); + + return TRUE; + } else if (sink->delegate->start(caps)) { + return TRUE; + } else { + return FALSE; + } +} + +gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *base, GstQuery *query) +{ + VO_SINK(base); + return sink->delegate->proposeAllocation(query); +} + +GstFlowReturn QGstVideoRendererSink::preroll(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + + gboolean showPreroll = true; // "show-preroll-frame" property is true by default + g_object_get(G_OBJECT(base), "show-preroll-frame", &showPreroll, NULL); + + return sink->delegate->render(buffer, showPreroll); // display frame +} + +GstFlowReturn QGstVideoRendererSink::render(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + return sink->delegate->render(buffer, true); +} + +QT_END_NAMESPACE diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp index f3e2d884c..147db6626 100644 --- a/src/gsttools/qvideosurfacegstsink.cpp +++ b/src/gsttools/qvideosurfacegstsink.cpp @@ -41,8 +41,13 @@ #include #include "qgstvideobuffer_p.h" +#include "qgstutils_p.h" #include "qvideosurfacegstsink_p.h" +#if GST_VERSION_MAJOR >=1 +#include +#endif + //#define DEBUG_VIDEO_SURFACE_SINK QT_BEGIN_NAMESPACE @@ -62,10 +67,12 @@ QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate( if (m_surface) { foreach (QObject *instance, bufferPoolLoader()->instances(QGstBufferPoolPluginKey)) { QGstBufferPoolInterface* plugin = qobject_cast(instance); + if (plugin) { m_pools.append(plugin); } } + updateSupportedFormats(); connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats())); } @@ -191,13 +198,15 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) m_format.frameSize(), m_format.pixelFormat()); - QVideoSurfaceGstSink::setFrameTimeStamps(&m_frame, buffer); + QGstUtils::setFrameTimeStamps(&m_frame, buffer); m_renderReturn = GST_FLOW_OK; if (QThread::currentThread() == thread()) { if (!m_surface.isNull()) m_surface->present(m_frame); + else + qWarning() << "m_surface.isNull()."; } else { QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection); m_renderCondition.wait(&m_mutex, 300); @@ -283,90 +292,6 @@ void QVideoSurfaceGstDelegate::updateSupportedFormats() } } -struct YuvFormat -{ - QVideoFrame::PixelFormat pixelFormat; - guint32 fourcc; - int bitsPerPixel; -}; - -static const YuvFormat qt_yuvColorLookup[] = -{ - { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, - { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, - { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, - { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, - { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, - { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 }, - { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 } -}; - -static int indexOfYuvColor(QVideoFrame::PixelFormat format) -{ - const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); - - for (int i = 0; i < count; ++i) - if (qt_yuvColorLookup[i].pixelFormat == format) - return i; - - return -1; -} - -static int indexOfYuvColor(guint32 fourcc) -{ - const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); - - for (int i = 0; i < count; ++i) - if (qt_yuvColorLookup[i].fourcc == fourcc) - return i; - - return -1; -} - -struct RgbFormat -{ - QVideoFrame::PixelFormat pixelFormat; - int bitsPerPixel; - int depth; - int endianness; - int red; - int green; - int blue; - int alpha; -}; - -static const RgbFormat qt_rgbColorLookup[] = -{ - { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x00000000 }, - { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, - { QVideoFrame::Format_BGR32 , 32, 24, 4321, int(0xFF000000), 0x00FF0000, 0x0000FF00, 0x00000000 }, - { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, - { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x000000FF }, - { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, int(0xFF000000) }, - { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, - { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, - { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } -}; - -static int indexOfRgbColor( - int bits, int depth, int endianness, int red, int green, int blue, int alpha) -{ - const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); - - for (int i = 0; i < count; ++i) { - if (qt_rgbColorLookup[i].bitsPerPixel == bits - && qt_rgbColorLookup[i].depth == depth - && qt_rgbColorLookup[i].endianness == endianness - && qt_rgbColorLookup[i].red == red - && qt_rgbColorLookup[i].green == green - && qt_rgbColorLookup[i].blue == blue - && qt_rgbColorLookup[i].alpha == alpha) { - return i; - } - } - return -1; -} - static GstVideoSinkClass *sink_parent_class; #define VO_SINK(s) QVideoSurfaceGstSink *sink(reinterpret_cast(s)) @@ -494,8 +419,6 @@ GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) { VO_SINK(base); - GstCaps *caps = gst_caps_new_empty(); - // Find the supported pixel formats // with buffer pool specific formats listed first QList supportedFormats; @@ -503,6 +426,7 @@ GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) QList poolHandleFormats; sink->delegate->poolMutex()->lock(); QGstBufferPoolInterface *pool = sink->delegate->pool(); + if (pool) poolHandleFormats = sink->delegate->supportedPixelFormats(pool->handleType()); sink->delegate->poolMutex()->unlock(); @@ -513,47 +437,7 @@ GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) supportedFormats.append(format); } - foreach (QVideoFrame::PixelFormat format, supportedFormats) { - int index = indexOfYuvColor(format); - - if (index != -1) { - gst_caps_append_structure(caps, gst_structure_new( - "video/x-raw-yuv", - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, - "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "format" , GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, - NULL)); - continue; - } - - const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); - - for (int i = 0; i < count; ++i) { - if (qt_rgbColorLookup[i].pixelFormat == format) { - GstStructure *structure = gst_structure_new( - "video/x-raw-rgb", - "framerate" , GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, - "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, - "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, - "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, - "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, - "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, - "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, - NULL); - - if (qt_rgbColorLookup[i].alpha != 0) { - gst_structure_set( - structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL); - } - gst_caps_append_structure(caps, structure); - } - } - } - - return caps; + return QGstUtils::capsForFormats(supportedFormats); } gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) @@ -575,7 +459,7 @@ gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) QAbstractVideoBuffer::HandleType handleType = pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle; - QVideoSurfaceFormat format = formatForCaps(caps, &bytesPerLine, handleType); + QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine, handleType); if (sink->delegate->isActive()) { QVideoSurfaceFormat surfaceFormst = sink->delegate->surfaceFormat(); @@ -592,7 +476,7 @@ gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) sink->lastRequestedCaps = 0; #ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << "Staring video surface, format:"; + qDebug() << "Starting video surface, format:"; qDebug() << format; qDebug() << "bytesPerLine:" << bytesPerLine; #endif @@ -606,87 +490,6 @@ gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) return FALSE; } -QVideoSurfaceFormat QVideoSurfaceGstSink::formatForCaps(GstCaps *caps, int *bytesPerLine, QAbstractVideoBuffer::HandleType handleType) -{ - const GstStructure *structure = gst_caps_get_structure(caps, 0); - - QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; - int bitsPerPixel = 0; - - QSize size; - gst_structure_get_int(structure, "width", &size.rwidth()); - gst_structure_get_int(structure, "height", &size.rheight()); - - if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { - guint32 fourcc = 0; - gst_structure_get_fourcc(structure, "format", &fourcc); - - int index = indexOfYuvColor(fourcc); - if (index != -1) { - pixelFormat = qt_yuvColorLookup[index].pixelFormat; - bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel; - } - } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { - int depth = 0; - int endianness = 0; - int red = 0; - int green = 0; - int blue = 0; - int alpha = 0; - - gst_structure_get_int(structure, "bpp", &bitsPerPixel); - gst_structure_get_int(structure, "depth", &depth); - gst_structure_get_int(structure, "endianness", &endianness); - gst_structure_get_int(structure, "red_mask", &red); - gst_structure_get_int(structure, "green_mask", &green); - gst_structure_get_int(structure, "blue_mask", &blue); - gst_structure_get_int(structure, "alpha_mask", &alpha); - - int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); - - if (index != -1) - pixelFormat = qt_rgbColorLookup[index].pixelFormat; - } - - if (pixelFormat != QVideoFrame::Format_Invalid) { - QVideoSurfaceFormat format(size, pixelFormat, handleType); - - QPair rate; - gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second); - - if (rate.second) - format.setFrameRate(qreal(rate.first)/rate.second); - - gint aspectNum = 0; - gint aspectDenum = 0; - if (gst_structure_get_fraction( - structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { - if (aspectDenum > 0) - format.setPixelAspectRatio(aspectNum, aspectDenum); - } - - if (bytesPerLine) - *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; - - return format; - } - - return QVideoSurfaceFormat(); -} - -void QVideoSurfaceGstSink::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) -{ - // GStreamer uses nanoseconds, Qt uses microseconds - qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); - if (startTime >= 0) { - frame->setStartTime(startTime/G_GINT64_CONSTANT (1000)); - - qint64 duration = GST_BUFFER_DURATION(buffer); - if (duration >= 0) - frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); - } -} - GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer) { @@ -731,7 +534,7 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( if (sink->delegate->isActive()) { //if format was changed, restart the surface - QVideoSurfaceFormat format = formatForCaps(intersection); + QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection); QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); if (format.pixelFormat() != surfaceFormat.pixelFormat() || @@ -749,7 +552,7 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( QAbstractVideoBuffer::HandleType handleType = pool ? pool->handleType() : QAbstractVideoBuffer::NoHandle; - QVideoSurfaceFormat format = formatForCaps(intersection, &bytesPerLine, handleType); + QVideoSurfaceFormat format = QGstUtils::formatForCaps(intersection, &bytesPerLine, handleType); if (!sink->delegate->start(format, bytesPerLine)) { qWarning() << "failed to start video surface"; @@ -763,7 +566,7 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); if (!pool->isFormatSupported(surfaceFormat)) { - //qDebug() << "sink doesn't support native pool format, skip custom buffers allocation"; + qDebug() << "sink doesn't support native pool format, skip custom buffers allocation"; return GST_FLOW_OK; } @@ -787,7 +590,6 @@ GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( gboolean QVideoSurfaceGstSink::start(GstBaseSink *base) { Q_UNUSED(base); - return TRUE; } diff --git a/src/multimedia/gsttools_headers/qgstappsrc_p.h b/src/multimedia/gsttools_headers/qgstappsrc_p.h index 4af92526f..0e0fc0acf 100644 --- a/src/multimedia/gsttools_headers/qgstappsrc_p.h +++ b/src/multimedia/gsttools_headers/qgstappsrc_p.h @@ -39,7 +39,10 @@ #include #include + +#if GST_VERSION_MAJOR < 1 #include +#endif QT_BEGIN_NAMESPACE diff --git a/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h b/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h index 34669b821..571a7ce5f 100644 --- a/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h +++ b/src/multimedia/gsttools_headers/qgstreameraudioprobecontrol_p.h @@ -38,23 +38,32 @@ #include #include #include +#include + +#include QT_BEGIN_NAMESPACE -class QGstreamerAudioProbeControl : public QMediaAudioProbeControl +class QGstreamerAudioProbeControl + : public QMediaAudioProbeControl + , public QGstreamerBufferProbe + , public QSharedData { Q_OBJECT public: explicit QGstreamerAudioProbeControl(QObject *parent); virtual ~QGstreamerAudioProbeControl(); - void bufferProbed(GstBuffer* buffer); +protected: + void probeCaps(GstCaps *caps); + bool probeBuffer(GstBuffer *buffer); private slots: void bufferProbed(); private: QAudioBuffer m_pendingBuffer; + QAudioFormat m_format; QMutex m_bufferMutex; }; diff --git a/src/multimedia/gsttools_headers/qgstreamerbufferprobe_p.h b/src/multimedia/gsttools_headers/qgstreamerbufferprobe_p.h new file mode 100644 index 000000000..924074269 --- /dev/null +++ b/src/multimedia/gsttools_headers/qgstreamerbufferprobe_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERBUFFERPROBE_H +#define QGSTREAMERBUFFERPROBE_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGstreamerBufferProbe +{ +public: + enum Flags + { + ProbeCaps = 0x01, + ProbeBuffers = 0x02, + ProbeAll = ProbeCaps | ProbeBuffers + }; + + explicit QGstreamerBufferProbe(Flags flags = ProbeAll); + virtual ~QGstreamerBufferProbe(); + + void addProbeToPad(GstPad *pad, bool downstream = true); + void removeProbeFromPad(GstPad *pad); + +protected: + virtual void probeCaps(GstCaps *caps); + virtual bool probeBuffer(GstBuffer *buffer); + +private: +#if GST_CHECK_VERSION(1,0,0) + static GstPadProbeReturn capsProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); + static GstPadProbeReturn bufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); + int m_capsProbeId; +#else + static gboolean bufferProbe(GstElement *element, GstBuffer *buffer, gpointer user_data); + GstCaps *m_caps; +#endif + int m_bufferProbeId; + const Flags m_flags; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERAUDIOPROBECONTROL_H diff --git a/src/multimedia/gsttools_headers/qgstreamermirtexturerenderer_p.h b/src/multimedia/gsttools_headers/qgstreamermirtexturerenderer_p.h new file mode 100644 index 000000000..25b8d60c2 --- /dev/null +++ b/src/multimedia/gsttools_headers/qgstreamermirtexturerenderer_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMIRTEXTURERENDERER_H +#define QGSTREAMERMIRTEXTURERENDERER_H + +#include +#include +#include +#include + +#include "qgstreamervideorendererinterface_p.h" + +QT_BEGIN_NAMESPACE + +class QGstreamerMirTextureBuffer; +class QGstreamerPlayerSession; +class QGLContext; +class QOpenGLContext; +class QSurfaceFormat; + +class QGstreamerMirTextureRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerMirTextureRenderer(QObject *parent = 0, const QGstreamerPlayerSession *playerSession = 0); + virtual ~QGstreamerMirTextureRenderer(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + void setPlayerSession(const QGstreamerPlayerSession *playerSession); + + GstElement *videoSink(); + + void stopRenderer(); + bool isReady() const { return m_surface != 0; } + +signals: + void sinkChanged(); + void readyChanged(bool); + void nativeSizeChanged(); + +private slots: + void handleFormatChange(); + void updateNativeVideoSize(); + void handleFocusWindowChanged(QWindow *window); + void renderFrame(); + +private: + QWindow *createOffscreenWindow(const QSurfaceFormat &format); + static void handleFrameReady(gpointer userData); + static GstPadProbeReturn padBufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer userData); + + GstElement *m_videoSink; + QPointer m_surface; + QPointer m_glSurface; + QGLContext *m_context; + QOpenGLContext *m_glContext; + unsigned int m_textureId; + QWindow *m_offscreenSurface; + QGstreamerPlayerSession *m_playerSession; + QGstreamerMirTextureBuffer *m_textureBuffer; + QSize m_nativeSize; + + QMutex m_mutex; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERMIRTEXTURERENDRER_H diff --git a/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h b/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h index 49064f977..f035f656b 100644 --- a/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h +++ b/src/multimedia/gsttools_headers/qgstreamervideoprobecontrol_p.h @@ -35,20 +35,29 @@ #define QGSTREAMERVIDEOPROBECONTROL_H #include +#include #include #include #include +#include + +#include QT_BEGIN_NAMESPACE -class QGstreamerVideoProbeControl : public QMediaVideoProbeControl +class QGstreamerVideoProbeControl + : public QMediaVideoProbeControl + , public QGstreamerBufferProbe + , public QSharedData { Q_OBJECT public: explicit QGstreamerVideoProbeControl(QObject *parent); virtual ~QGstreamerVideoProbeControl(); - void bufferProbed(GstBuffer* buffer); + void probeCaps(GstCaps *caps); + bool probeBuffer(GstBuffer *buffer); + void startFlushing(); void stopFlushing(); @@ -56,10 +65,16 @@ private slots: void frameProbed(); private: - bool m_flushing; - bool m_frameProbed; // true if at least one frame was probed + QVideoSurfaceFormat m_format; QVideoFrame m_pendingFrame; QMutex m_frameMutex; +#if GST_CHECK_VERSION(1,0,0) + GstVideoInfo m_videoInfo; +#else + int m_bytesPerLine; +#endif + bool m_flushing; + bool m_frameProbed; // true if at least one frame was probed }; QT_END_NAMESPACE diff --git a/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h b/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h index 81e576405..d38156c14 100644 --- a/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h +++ b/src/multimedia/gsttools_headers/qgstreamervideowindow_p.h @@ -38,6 +38,7 @@ #include "qgstreamervideorendererinterface_p.h" #include +#include #include QT_BEGIN_NAMESPACE @@ -45,7 +46,8 @@ class QAbstractVideoSurface; class QGstreamerVideoWindow : public QVideoWindowControl, public QGstreamerVideoRendererInterface, - public QGstreamerSyncMessageFilter + public QGstreamerSyncMessageFilter, + private QGstreamerBufferProbe { Q_OBJECT Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter) @@ -101,10 +103,10 @@ signals: void readyChanged(bool); private slots: - void updateNativeVideoSize(); + void updateNativeVideoSize(const QSize &size); private: - static void padBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); + void probeCaps(GstCaps *caps); GstElement *m_videoSink; WId m_windowId; @@ -113,7 +115,6 @@ private: bool m_fullScreen; QSize m_nativeSize; mutable QColor m_colorKey; - int m_bufferProbeId; }; QT_END_NAMESPACE diff --git a/src/multimedia/gsttools_headers/qgstutils_p.h b/src/multimedia/gsttools_headers/qgstutils_p.h index 65ff759aa..71a0a57f4 100644 --- a/src/multimedia/gsttools_headers/qgstutils_p.h +++ b/src/multimedia/gsttools_headers/qgstutils_p.h @@ -49,14 +49,32 @@ #include #include #include +#include #include #include +#include +#include +#include + +#if GST_CHECK_VERSION(1,0,0) +# define QT_GSTREAMER_PLAYBIN_ELEMENT_NAME "playbin" +# define QT_GSTREAMER_CAMERABIN_ELEMENT_NAME "camerabin" +# define QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME "videoconvert" +# define QT_GSTREAMER_RAW_AUDIO_MIME "audio/x-raw" +#else +# define QT_GSTREAMER_PLAYBIN_ELEMENT_NAME "playbin2" +# define QT_GSTREAMER_CAMERABIN_ELEMENT_NAME "camerabin2" +# define QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME "ffmpegcolorspace" +# define QT_GSTREAMER_RAW_AUDIO_MIME "audio/x-raw-int" +#endif QT_BEGIN_NAMESPACE class QSize; class QVariant; class QByteArray; +class QImage; +class QVideoSurfaceFormat; namespace QGstUtils { struct CameraInfo @@ -73,8 +91,12 @@ namespace QGstUtils { QSize capsResolution(const GstCaps *caps); QSize capsCorrectedResolution(const GstCaps *caps); QAudioFormat audioFormatForCaps(const GstCaps *caps); +#if GST_CHECK_VERSION(1,0,0) + QAudioFormat audioFormatForSample(GstSample *sample); +#else QAudioFormat audioFormatForBuffer(GstBuffer *buffer); - GstCaps *capsForAudioFormat(QAudioFormat format); +#endif + GstCaps *capsForAudioFormat(const QAudioFormat &format); void initializeGst(); QMultimedia::SupportEstimate hasSupport(const QString &mimeType, const QStringList &codecs, @@ -86,9 +108,40 @@ namespace QGstUtils { QCamera::Position cameraPosition(const QString &device, GstElementFactory * factory = 0); int cameraOrientation(const QString &device, GstElementFactory * factory = 0); QByteArray cameraDriver(const QString &device, GstElementFactory * factory = 0); + + QSet supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)); + +#if GST_CHECK_VERSION(1,0,0) + QImage bufferToImage(GstBuffer *buffer, const GstVideoInfo &info); + QVideoSurfaceFormat formatForCaps( + GstCaps *caps, + GstVideoInfo *info, + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); +#else + QImage bufferToImage(GstBuffer *buffer); + QVideoSurfaceFormat formatForCaps( + GstCaps *caps, + int *bytesPerLine = 0, + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); +#endif + + GstCaps *capsForFormats(const QList &formats); + void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); + + void setMetaData(GstElement *element, const QMap &data); + void setMetaData(GstBin *bin, const QMap &data); + + GstCaps *videoFilterCaps(); + } void qt_gst_object_ref_sink(gpointer object); +GstCaps *qt_gst_pad_get_current_caps(GstPad *pad); +GstStructure *qt_gst_structure_new_empty(const char *name); +gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur); +gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur); + +QDebug operator <<(QDebug debug, GstCaps *caps); QT_END_NAMESPACE diff --git a/src/multimedia/gsttools_headers/qgstvideobuffer_p.h b/src/multimedia/gsttools_headers/qgstvideobuffer_p.h index 1e0fda84d..00aca484a 100644 --- a/src/multimedia/gsttools_headers/qgstvideobuffer_p.h +++ b/src/multimedia/gsttools_headers/qgstvideobuffer_p.h @@ -49,26 +49,47 @@ #include #include +#include QT_BEGIN_NAMESPACE +#if GST_CHECK_VERSION(1,0,0) +class QGstVideoBuffer : public QAbstractPlanarVideoBuffer +{ +public: + QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info); + QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, + HandleType handleType, const QVariant &handle); +#else class QGstVideoBuffer : public QAbstractVideoBuffer { public: QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine); QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, HandleType handleType, const QVariant &handle); +#endif + ~QGstVideoBuffer(); MapMode mapMode() const; +#if GST_CHECK_VERSION(1,0,0) + int map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]); +#else uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); +#endif + void unmap(); QVariant handle() const { return m_handle; } private: - GstBuffer *m_buffer; +#if GST_CHECK_VERSION(1,0,0) + GstVideoInfo m_videoInfo; + GstVideoFrame m_frame; +#else int m_bytesPerLine; +#endif + GstBuffer *m_buffer; MapMode m_mode; QVariant m_handle; }; diff --git a/src/multimedia/gsttools_headers/qgstvideorendererplugin_p.h b/src/multimedia/gsttools_headers/qgstvideorendererplugin_p.h new file mode 100644 index 000000000..616677a57 --- /dev/null +++ b/src/multimedia/gsttools_headers/qgstvideorendererplugin_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTVIDEORENDERERPLUGIN_P_H +#define QGSTVIDEORENDERERPLUGIN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QAbstractVideoSurface; + +const QLatin1String QGstVideoRendererPluginKey("gstvideorenderer"); + +class QGstVideoRenderer +{ +public: + virtual ~QGstVideoRenderer() {} + + virtual GstCaps *getCaps(QAbstractVideoSurface *surface) = 0; + virtual bool start(QAbstractVideoSurface *surface, GstCaps *caps) = 0; + virtual void stop(QAbstractVideoSurface *surface) = 0; // surface may be null if unexpectedly deleted. + virtual bool proposeAllocation(GstQuery *query) = 0; // may be called from a thread. + + virtual bool present(QAbstractVideoSurface *surface, GstBuffer *buffer) = 0; + virtual void flush(QAbstractVideoSurface *surface) = 0; // surface may be null if unexpectedly deleted. +}; + +/* + Abstract interface for video buffers allocation. +*/ +class QGstVideoRendererInterface +{ +public: + virtual ~QGstVideoRendererInterface() {} + + virtual QGstVideoRenderer *createRenderer() = 0; +}; + +#define QGstVideoRendererInterface_iid "org.qt-project.qt.gstvideorenderer/5.4" +Q_DECLARE_INTERFACE(QGstVideoRendererInterface, QGstVideoRendererInterface_iid) + +class QGstVideoRendererPlugin : public QObject, public QGstVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstVideoRendererInterface) +public: + explicit QGstVideoRendererPlugin(QObject *parent = 0); + virtual ~QGstVideoRendererPlugin() {} + + virtual QGstVideoRenderer *createRenderer() = 0; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h new file mode 100644 index 000000000..6feb371c4 --- /dev/null +++ b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTVIDEORENDERERSINK_P_H +#define QGSTVIDEORENDERERSINK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qgstvideorendererplugin_p.h" + +#include "qgstvideorendererplugin_p.h" + +QT_BEGIN_NAMESPACE +class QAbstractVideoSurface; + +class QGstDefaultVideoRenderer : public QGstVideoRenderer +{ +public: + QGstDefaultVideoRenderer(); + ~QGstDefaultVideoRenderer(); + + GstCaps *getCaps(QAbstractVideoSurface *surface); + bool start(QAbstractVideoSurface *surface, GstCaps *caps); + void stop(QAbstractVideoSurface *surface); + + bool proposeAllocation(GstQuery *query); + + bool present(QAbstractVideoSurface *surface, GstBuffer *buffer); + void flush(QAbstractVideoSurface *surface); + +private: + QVideoSurfaceFormat m_format; + GstVideoInfo m_videoInfo; + bool m_flushed; +}; + +class QVideoSurfaceGstDelegate : public QObject +{ + Q_OBJECT +public: + QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface); + ~QVideoSurfaceGstDelegate(); + + GstCaps *caps(); + + bool start(GstCaps *caps); + void stop(); + bool proposeAllocation(GstQuery *query); + + void flush(); + + GstFlowReturn render(GstBuffer *buffer, bool show); + + bool event(QEvent *event); + + static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); + +private slots: + bool handleEvent(QMutexLocker *locker); + void updateSupportedFormats(); + +private: + void notify(); + bool waitForAsyncEvent(QMutexLocker *locker, QWaitCondition *condition, unsigned long time); + + QPointer m_surface; + + QMutex m_mutex; + QWaitCondition m_setupCondition; + QWaitCondition m_renderCondition; + GstFlowReturn m_renderReturn; + QList m_renderers; + QGstVideoRenderer *m_renderer; + QGstVideoRenderer *m_activeRenderer; + + GstCaps *m_surfaceCaps; + GstCaps *m_startCaps; + GstBuffer *m_lastBuffer; + + bool m_notified; + bool m_stop; + bool m_render; + bool m_flush; +}; + +class QGstVideoRendererSink +{ +public: + GstVideoSink parent; + + static QGstVideoRendererSink *createSink(QAbstractVideoSurface *surface); + +private: + static GType get_type(); + static void class_init(gpointer g_class, gpointer class_data); + static void base_init(gpointer g_class); + static void instance_init(GTypeInstance *instance, gpointer g_class); + + static void finalize(GObject *object); + + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); + + static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter); + static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); + + static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query); + + static GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer); + static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); + +private: + QVideoSurfaceGstDelegate *delegate; +}; + + +class QGstVideoRendererSinkClass +{ +public: + GstVideoSinkClass parent_class; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h index 11b305d27..0ea18c0ff 100644 --- a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h +++ b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h @@ -45,6 +45,18 @@ // We mean it. // +#include + +#if GST_CHECK_VERSION(1,0,0) + +#include "qgstvideorenderersink_p.h" + +QT_BEGIN_NAMESPACE +typedef QGstVideoRendererSink QVideoSurfaceGstSink; +QT_END_NAMESPACE + +#else + #include #include @@ -116,10 +128,6 @@ public: GstVideoSink parent; static QVideoSurfaceGstSink *createSink(QAbstractVideoSurface *surface); - static QVideoSurfaceFormat formatForCaps(GstCaps *caps, - int *bytesPerLine = 0, - QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); - static void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); private: static GType get_type(); @@ -150,7 +158,6 @@ private: QVideoSurfaceFormat *lastSurfaceFormat; }; - class QVideoSurfaceGstSinkClass { public: @@ -160,3 +167,5 @@ public: QT_END_NAMESPACE #endif + +#endif diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro index b3bdaa874..ff47768ab 100644 --- a/src/multimedia/multimedia.pro +++ b/src/multimedia/multimedia.pro @@ -4,6 +4,8 @@ QT = core-private network gui-private MODULE_PLUGIN_TYPES = \ mediaservice \ audio \ + video/bufferpool \ + video/gstvideorenderer \ video/videonode \ playlistformats diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp index 3098aab9d..befbb9ae2 100644 --- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp +++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecoderserviceplugin.cpp @@ -68,89 +68,16 @@ QMultimedia::SupportEstimate QGstreamerAudioDecoderServicePlugin::hasSupport(con return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet); } -void QGstreamerAudioDecoderServicePlugin::updateSupportedMimeTypes() const +static bool isDecoderOrDemuxer(GstElementFactory *factory) { - //enumerate supported mime types - gst_init(NULL, NULL); - - GList *plugins, *orig_plugins; - orig_plugins = plugins = gst_default_registry_get_plugin_list (); - - while (plugins) { - GList *features, *orig_features; - - GstPlugin *plugin = (GstPlugin *) (plugins->data); - plugins = g_list_next (plugins); - - if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED - continue; - - orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get_default (), - plugin->desc.name); - while (features) { - if (!G_UNLIKELY(features->data == NULL)) { - GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); - if (GST_IS_ELEMENT_FACTORY (feature)) { - GstElementFactory *factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); - if (factory - && factory->numpadtemplates > 0 - && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0 - || qstrcmp(factory->details.klass, "Codec/Demux") == 0 )) { - const GList *pads = factory->staticpadtemplates; - while (pads) { - GstStaticPadTemplate *padtemplate = (GstStaticPadTemplate*)(pads->data); - pads = g_list_next (pads); - if (padtemplate->direction != GST_PAD_SINK) - continue; - if (padtemplate->static_caps.string) { - GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); - if (!gst_caps_is_any (caps) && ! gst_caps_is_empty (caps)) { - for (guint i = 0; i < gst_caps_get_size(caps); i++) { - GstStructure *structure = gst_caps_get_structure(caps, i); - QString nameLowcase = QString(gst_structure_get_name (structure)).toLower(); - - m_supportedMimeTypeSet.insert(nameLowcase); - if (nameLowcase.contains("mpeg")) { - //Because mpeg version number is only included in the detail - //description, it is necessary to manually extract this information - //in order to match the mime type of mpeg4. - const GValue *value = gst_structure_get_value(structure, "mpegversion"); - if (value) { - gchar *str = gst_value_serialize (value); - QString versions(str); - QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); - foreach (const QString &e, elements) - m_supportedMimeTypeSet.insert(nameLowcase + e); - g_free (str); - } - } - } - } - gst_caps_unref(caps); - } - } - gst_object_unref (factory); - } - } else if (GST_IS_TYPE_FIND_FACTORY(feature)) { - QString name(gst_plugin_feature_get_name(feature)); - if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type - m_supportedMimeTypeSet.insert(name.toLower()); - } - } - features = g_list_next (features); - } - gst_plugin_feature_list_free (orig_features); - } - gst_plugin_list_free (orig_plugins); + return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DEMUXER) + || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DECODER + | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO); +} -#if defined QT_SUPPORTEDMIMETYPES_DEBUG - QStringList list = m_supportedMimeTypeSet.toList(); - list.sort(); - if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { - foreach (const QString &type, list) - qDebug() << type; - } -#endif +void QGstreamerAudioDecoderServicePlugin::updateSupportedMimeTypes() const +{ + m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isDecoderOrDemuxer); } QStringList QGstreamerAudioDecoderServicePlugin::supportedMimeTypes() const diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp index f944a60ac..69876b963 100644 --- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp +++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp @@ -85,7 +85,7 @@ QGstreamerAudioDecoderSession::QGstreamerAudioDecoderSession(QObject *parent) m_durationQueries(0) { // Create pipeline here - m_playbin = gst_element_factory_make("playbin2", NULL); + m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, NULL); if (m_playbin != 0) { // Sort out messages @@ -446,21 +446,40 @@ QAudioBuffer QGstreamerAudioDecoderSession::read() if (buffersAvailable == 1) emit bufferAvailableChanged(false); + const char* bufferData = 0; + int bufferSize = 0; + +#if GST_CHECK_VERSION(1,0,0) + GstSample *sample = gst_app_sink_pull_sample(m_appSink); + GstBuffer *buffer = gst_sample_get_buffer(sample); + GstMapInfo mapInfo; + gst_buffer_map(buffer, &mapInfo, GST_MAP_READ); + bufferData = (const char*)mapInfo.data; + bufferSize = mapInfo.size; + QAudioFormat format = QGstUtils::audioFormatForSample(sample); +#else GstBuffer *buffer = gst_app_sink_pull_buffer(m_appSink); - + bufferData = (const char*)buffer->data; + bufferSize = buffer->size; QAudioFormat format = QGstUtils::audioFormatForBuffer(buffer); +#endif + if (format.isValid()) { // XXX At the moment we have to copy data from GstBuffer into QAudioBuffer. // We could improve performance by implementing QAbstractAudioBuffer for GstBuffer. qint64 position = getPositionFromBuffer(buffer); - audioBuffer = QAudioBuffer(QByteArray((const char*)buffer->data, buffer->size), format, position); + audioBuffer = QAudioBuffer(QByteArray((const char*)bufferData, bufferSize), format, position); position /= 1000; // convert to milliseconds if (position != m_position) { m_position = position; emit positionChanged(m_position); } } +#if GST_CHECK_VERSION(1,0,0) + gst_sample_unref(sample); +#else gst_buffer_unref(buffer); +#endif } return audioBuffer; @@ -488,7 +507,7 @@ void QGstreamerAudioDecoderSession::processInvalidMedia(QAudioDecoder::Error err emit error(int(errorCode), errorString); } -GstFlowReturn QGstreamerAudioDecoderSession::new_buffer(GstAppSink *, gpointer user_data) +GstFlowReturn QGstreamerAudioDecoderSession::new_sample(GstAppSink *, gpointer user_data) { // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()." QGstreamerAudioDecoderSession *session = reinterpret_cast(user_data); @@ -531,7 +550,11 @@ void QGstreamerAudioDecoderSession::addAppSink() GstAppSinkCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); - callbacks.new_buffer = &new_buffer; +#if GST_CHECK_VERSION(1,0,0) + callbacks.new_sample = &new_sample; +#else + callbacks.new_buffer = &new_sample; +#endif gst_app_sink_set_callbacks(m_appSink, &callbacks, this, NULL); gst_app_sink_set_max_buffers(m_appSink, MAX_BUFFERS_IN_QUEUE); gst_base_sink_set_sync(GST_BASE_SINK(m_appSink), FALSE); @@ -553,11 +576,10 @@ void QGstreamerAudioDecoderSession::removeAppSink() void QGstreamerAudioDecoderSession::updateDuration() { - GstFormat format = GST_FORMAT_TIME; gint64 gstDuration = 0; int duration = -1; - if (m_playbin && gst_element_query_duration(m_playbin, &format, &gstDuration)) + if (m_playbin && qt_gst_element_query_duration(m_playbin, GST_FORMAT_TIME, &gstDuration)) duration = gstDuration / 1000000; if (m_duration != duration) { diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h index 091219666..068221c93 100644 --- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h +++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h @@ -92,7 +92,7 @@ public: qint64 position() const; qint64 duration() const; - static GstFlowReturn new_buffer(GstAppSink *sink, gpointer user_data); + static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data); signals: void stateChanged(QAudioDecoder::State newState); diff --git a/src/plugins/gstreamer/camerabin/camerabin.pro b/src/plugins/gstreamer/camerabin/camerabin.pro index bba797f5e..64fee3e41 100644 --- a/src/plugins/gstreamer/camerabin/camerabin.pro +++ b/src/plugins/gstreamer/camerabin/camerabin.pro @@ -79,7 +79,7 @@ config_gstreamer_photography { $$PWD/camerabinlocks.cpp \ $$PWD/camerabinzoom.cpp - LIBS += -lgstphotography-0.10 + LIBS += -lgstphotography-$$GST_VERSION DEFINES += GST_USE_UNSTABLE_API #prevents warnings because of unstable photography API } diff --git a/src/plugins/gstreamer/camerabin/camerabincontainer.cpp b/src/plugins/gstreamer/camerabin/camerabincontainer.cpp index ebb914b60..9531f0130 100644 --- a/src/plugins/gstreamer/camerabin/camerabincontainer.cpp +++ b/src/plugins/gstreamer/camerabin/camerabincontainer.cpp @@ -96,7 +96,7 @@ GstEncodingContainerProfile *CameraBinContainer::createProfile() GstCaps *caps; if (m_actualFormat.isEmpty()) { - caps = gst_caps_new_any(); + return 0; } else { QString format = m_actualFormat; QStringList supportedFormats = m_supportedContainers.supportedCodecs(); diff --git a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp index 3ec992791..8c6b8b0ea 100644 --- a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp +++ b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp @@ -95,11 +95,6 @@ void CameraBinControl::setCaptureMode(QCamera::CaptureModes mode) captureMode() == QCamera::CaptureStillImage ? CamerabinResourcePolicy::ImageCaptureResources : CamerabinResourcePolicy::VideoCaptureResources); -#if (GST_VERSION_MAJOR == 0) && ((GST_VERSION_MINOR < 10) || (GST_VERSION_MICRO < 23)) - //due to bug in v4l2src, it's necessary to reload camera on video caps changes - //https://bugzilla.gnome.org/show_bug.cgi?id=649832 - reloadLater(); -#endif } emit captureModeChanged(mode); } @@ -299,6 +294,8 @@ bool CameraBinControl::canChangeProperty(PropertyChangeType changeType, QCamera: switch (changeType) { case QCameraControl::CaptureMode: + return status != QCamera::ActiveStatus; + break; case QCameraControl::ImageEncodingSettings: case QCameraControl::VideoEncodingSettings: case QCameraControl::Viewfinder: diff --git a/src/plugins/gstreamer/camerabin/camerabinexposure.cpp b/src/plugins/gstreamer/camerabin/camerabinexposure.cpp index a235de2f2..795fd4275 100644 --- a/src/plugins/gstreamer/camerabin/camerabinexposure.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinexposure.cpp @@ -37,6 +37,10 @@ #include +#if !GST_CHECK_VERSION(1,0,0) +typedef GstSceneMode GstPhotographySceneMode; +#endif + QT_BEGIN_NAMESPACE CameraBinExposure::CameraBinExposure(CameraBinSession *session) @@ -119,7 +123,7 @@ QVariant CameraBinExposure::actualValue(ExposureParameter parameter) const } case QCameraExposureControl::ExposureMode: { - GstSceneMode sceneMode; + GstPhotographySceneMode sceneMode; gst_photography_get_scene_mode(m_session->photography(), &sceneMode); switch (sceneMode) { @@ -167,7 +171,7 @@ bool CameraBinExposure::setValue(ExposureParameter parameter, const QVariant& va case QCameraExposureControl::ExposureMode: { QCameraExposure::ExposureMode mode = QCameraExposure::ExposureMode(value.toInt()); - GstSceneMode sceneMode; + GstPhotographySceneMode sceneMode; gst_photography_get_scene_mode(m_session->photography(), &sceneMode); switch (mode) { diff --git a/src/plugins/gstreamer/camerabin/camerabinflash.cpp b/src/plugins/gstreamer/camerabin/camerabinflash.cpp index 2140f6684..51bb9a27d 100644 --- a/src/plugins/gstreamer/camerabin/camerabinflash.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinflash.cpp @@ -37,6 +37,10 @@ #include +#if !GST_CHECK_VERSION(1,0,0) +typedef GstFlashMode GstPhotographyFlashMode; +#endif + QT_BEGIN_NAMESPACE CameraBinFlash::CameraBinFlash(CameraBinSession *session) @@ -51,7 +55,7 @@ CameraBinFlash::~CameraBinFlash() QCameraExposure::FlashModes CameraBinFlash::flashMode() const { - GstFlashMode flashMode; + GstPhotographyFlashMode flashMode; gst_photography_get_flash_mode(m_session->photography(), &flashMode); QCameraExposure::FlashModes modes; @@ -70,7 +74,7 @@ QCameraExposure::FlashModes CameraBinFlash::flashMode() const void CameraBinFlash::setFlashMode(QCameraExposure::FlashModes mode) { - GstFlashMode flashMode; + GstPhotographyFlashMode flashMode; gst_photography_get_flash_mode(m_session->photography(), &flashMode); if (mode.testFlag(QCameraExposure::FlashAuto)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO; diff --git a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp index 665e20443..061c68044 100644 --- a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp @@ -39,6 +39,12 @@ #include #include +#include + +#if !GST_CHECK_VERSION(1,0,0) +typedef GstFocusMode GstPhotographyFocusMode; +#endif + //#define CAMERABIN_DEBUG 1 QT_BEGIN_NAMESPACE @@ -73,7 +79,7 @@ QCameraFocus::FocusModes CameraBinFocus::focusMode() const void CameraBinFocus::setFocusMode(QCameraFocus::FocusModes mode) { - GstFocusMode photographyMode; + GstPhotographyFocusMode photographyMode; switch (mode) { case QCameraFocus::AutoFocus: @@ -181,9 +187,10 @@ QCameraFocusZoneList CameraBinFocus::focusZones() const void CameraBinFocus::handleFocusMessage(GstMessage *gm) { //it's a sync message, so it's called from non main thread - if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) { + const GstStructure *structure = gst_message_get_structure(gm); + if (gst_structure_has_name(structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) { gint status = GST_PHOTOGRAPHY_FOCUS_STATUS_NONE; - gst_structure_get_int (gm->structure, "status", &status); + gst_structure_get_int (structure, "status", &status); QCamera::LockStatus focusStatus = m_focusStatus; QCamera::LockChangeReason reason = QCamera::UserRequest; @@ -243,7 +250,7 @@ void CameraBinFocus::_q_handleCameraStateChange(QCamera::State state) m_cameraState = state; if (state == QCamera::ActiveState) { if (GstPad *pad = gst_element_get_static_pad(m_session->cameraSource(), "vfsrc")) { - if (GstCaps *caps = gst_pad_get_negotiated_caps(pad)) { + if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) { if (GstStructure *structure = gst_caps_get_structure(caps, 0)) { int width = 0; int height = 0; diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp index 695215592..8b5130641 100644 --- a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp @@ -53,11 +53,13 @@ QT_BEGIN_NAMESPACE CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session) :QCameraImageCaptureControl(session) + , m_encoderProbe(this) + , m_muxerProbe(this) , m_session(session) - , m_ready(false) - , m_requestId(0) , m_jpegEncoderElement(0) , m_metadataMuxerElement(0) + , m_requestId(0) + , m_ready(false) { connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState())); connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int))); @@ -108,11 +110,18 @@ void CameraBinImageCapture::updateState() } } -gboolean CameraBinImageCapture::metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *self) +#if GST_CHECK_VERSION(1,0,0) +GstPadProbeReturn CameraBinImageCapture::encoderEventProbe( + GstPad *, GstPadProbeInfo *info, gpointer user_data) { - Q_UNUSED(pad); - - if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { + GstEvent * const event = gst_pad_probe_info_get_event(info); +#else +gboolean CameraBinImageCapture::encoderEventProbe( + GstElement *, GstEvent *event, gpointer user_data) +{ +#endif + CameraBinImageCapture * const self = static_cast(user_data); + if (event && GST_EVENT_TYPE(event) == GST_EVENT_TAG) { GstTagList *gstTags; gst_event_parse_tag(event, &gstTags); QMap extendedTags = QGstUtils::gstTagListToMap(gstTags); @@ -146,17 +155,31 @@ gboolean CameraBinImageCapture::metadataEventProbe(GstPad *pad, GstEvent *event, } } } +#if GST_CHECK_VERSION(1,0,0) + return GST_PAD_PROBE_OK; +#else + return TRUE; +#endif +} - return true; +void CameraBinImageCapture::EncoderProbe::probeCaps(GstCaps *caps) +{ +#if GST_CHECK_VERSION(1,0,0) + capture->m_bufferFormat = QGstUtils::formatForCaps(caps, &capture->m_videoInfo); +#else + int bytesPerLine = 0; + QVideoSurfaceFormat format = QGstUtils::formatForCaps(caps, &bytesPerLine); + capture->m_bytesPerLine = bytesPerLine; + capture->m_bufferFormat = format; +#endif } -gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self) +bool CameraBinImageCapture::EncoderProbe::probeBuffer(GstBuffer *buffer) { - Q_UNUSED(pad); - CameraBinSession *session = self->m_session; + CameraBinSession * const session = capture->m_session; #ifdef DEBUG_CAPTURE - qDebug() << "Uncompressed buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer)); + qDebug() << "Uncompressed buffer probe"; #endif QCameraImageCapture::CaptureDestinations destination = @@ -165,21 +188,23 @@ gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer * if (destination & QCameraImageCapture::CaptureToBuffer) { if (format != QVideoFrame::Format_Jpeg) { - GstCaps *caps = GST_BUFFER_CAPS(buffer); - int bytesPerLine = -1; - QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine); #ifdef DEBUG_CAPTURE qDebug() << "imageAvailable(uncompressed):" << format; #endif - QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, bytesPerLine); +#if GST_CHECK_VERSION(1,0,0) + QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, capture->m_videoInfo); +#else + QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, capture->m_bytesPerLine); +#endif - QVideoFrame frame(videoBuffer, - format.frameSize(), - format.pixelFormat()); + QVideoFrame frame( + videoBuffer, + capture->m_bufferFormat.frameSize(), + capture->m_bufferFormat.pixelFormat()); - QMetaObject::invokeMethod(self, "imageAvailable", + QMetaObject::invokeMethod(capture, "imageAvailable", Qt::QueuedConnection, - Q_ARG(int, self->m_requestId), + Q_ARG(int, capture->m_requestId), Q_ARG(QVideoFrame, frame)); } } @@ -192,25 +217,40 @@ gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer * return keepBuffer; } -gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self) +void CameraBinImageCapture::MuxerProbe::probeCaps(GstCaps *caps) { - Q_UNUSED(pad); - CameraBinSession *session = self->m_session; + capture->m_jpegResolution = QGstUtils::capsCorrectedResolution(caps); +} -#ifdef DEBUG_CAPTURE - qDebug() << "Jpeg buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer)); -#endif +bool CameraBinImageCapture::MuxerProbe::probeBuffer(GstBuffer *buffer) +{ + CameraBinSession * const session = capture->m_session; QCameraImageCapture::CaptureDestinations destination = session->captureDestinationControl()->captureDestination(); if ((destination & QCameraImageCapture::CaptureToBuffer) && session->captureBufferFormatControl()->bufferFormat() == QVideoFrame::Format_Jpeg) { - QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, - -1); //bytesPerLine is not available for jpegs - QSize resolution = QGstUtils::capsCorrectedResolution(GST_BUFFER_CAPS(buffer)); + QSize resolution = capture->m_jpegResolution; //if resolution is not presented in caps, try to find it from encoded jpeg data: +#if GST_CHECK_VERSION(1,0,0) + GstMapInfo mapInfo; + if (resolution.isEmpty() && gst_buffer_map(buffer, &mapInfo, GST_MAP_READ)) { + QBuffer data; + data.setData(reinterpret_cast(mapInfo.data), mapInfo.size); + + QImageReader reader(&data, "JPEG"); + resolution = reader.size(); + + gst_buffer_unmap(buffer, &mapInfo); + } + + GstVideoInfo info; + gst_video_info_set_format( + &info, GST_VIDEO_FORMAT_ENCODED, resolution.width(), resolution.height()); + QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, info); +#else if (resolution.isEmpty()) { QBuffer data; data.setData(reinterpret_cast(GST_BUFFER_DATA(buffer)), GST_BUFFER_SIZE(buffer)); @@ -218,20 +258,28 @@ gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, resolution = reader.size(); } + QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, + -1); //bytesPerLine is not available for jpegs +#endif + + QVideoFrame frame(videoBuffer, resolution, QVideoFrame::Format_Jpeg); - - QMetaObject::invokeMethod(self, "imageAvailable", + QMetaObject::invokeMethod(capture, "imageAvailable", Qt::QueuedConnection, - Q_ARG(int, self->m_requestId), + Q_ARG(int, capture->m_requestId), Q_ARG(QVideoFrame, frame)); } - //drop the buffer if capture to file was disabled - return destination & QCameraImageCapture::CaptureToFile; + + // Theoretically we could drop the buffer here when don't want to capture to file but that + // prevents camerabin from recognizing that capture has been completed and returning + // to its idle state. + return true; } + bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) { //Install metadata event and buffer probes @@ -252,9 +300,10 @@ bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) return false; QString elementName = QString::fromLatin1(gst_element_get_name(element)); +#if !GST_CHECK_VERSION(1,0,0) GstElementClass *elementClass = GST_ELEMENT_GET_CLASS(element); QString elementLongName = elementClass->details.longname; - +#endif if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) { m_jpegEncoderElement = element; GstPad *sinkpad = gst_element_get_static_pad(element, "sink"); @@ -264,21 +313,23 @@ bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) #ifdef DEBUG_CAPTURE qDebug() << "install metadata probe"; #endif - gst_pad_add_event_probe(sinkpad, - G_CALLBACK(CameraBinImageCapture::metadataEventProbe), - this); - +#if GST_CHECK_VERSION(1,0,0) + gst_pad_add_probe( + sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, encoderEventProbe, this, NULL); +#else + gst_pad_add_event_probe(sinkpad, G_CALLBACK(encoderEventProbe), this); +#endif #ifdef DEBUG_CAPTURE qDebug() << "install uncompressed buffer probe"; #endif - gst_pad_add_buffer_probe(sinkpad, - G_CALLBACK(CameraBinImageCapture::uncompressedBufferProbe), - this); + m_encoderProbe.addProbeToPad(sinkpad, true); gst_object_unref(sinkpad); - } else if ((elementName.contains("jifmux") || - elementName.startsWith("metadatamux") || - elementLongName == QLatin1String("JPEG stream muxer")) + } else if ((elementName.contains("jifmux") +#if !GST_CHECK_VERSION(1,0,0) + || elementLongName == QLatin1String("JPEG stream muxer") +#endif + || elementName.startsWith("metadatamux")) && element != m_metadataMuxerElement) { //Jpeg encoded buffer probe is added after jifmux/metadatamux //element to ensure the resulting jpeg buffer contains capture metadata @@ -288,9 +339,8 @@ bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) #ifdef DEBUG_CAPTURE qDebug() << "install jpeg buffer probe"; #endif - gst_pad_add_buffer_probe(srcpad, - G_CALLBACK(CameraBinImageCapture::jpegBufferProbe), - this); + m_muxerProbe.addProbeToPad(srcpad); + gst_object_unref(srcpad); } } diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.h b/src/plugins/gstreamer/camerabin/camerabinimagecapture.h index c2e26f51c..9a52dd9da 100644 --- a/src/plugins/gstreamer/camerabin/camerabinimagecapture.h +++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.h @@ -38,6 +38,14 @@ #include #include "camerabinsession.h" +#include + +#include + +#if GST_CHECK_VERSION(1,0,0) +#include +#endif + QT_BEGIN_NAMESPACE class CameraBinImageCapture : public QCameraImageCaptureControl, public QGstreamerBusMessageFilter @@ -61,15 +69,47 @@ private slots: void updateState(); private: - static gboolean metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *); - static gboolean uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); - static gboolean jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); +#if GST_CHECK_VERSION(1,0,0) + static GstPadProbeReturn encoderEventProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data); +#else + static gboolean encoderEventProbe(GstElement *, GstEvent *event, gpointer user_data); +#endif + + class EncoderProbe : public QGstreamerBufferProbe + { + public: + EncoderProbe(CameraBinImageCapture *capture) : capture(capture) {} + void probeCaps(GstCaps *caps); + bool probeBuffer(GstBuffer *buffer); + + private: + CameraBinImageCapture * const capture; + } m_encoderProbe; + + class MuxerProbe : public QGstreamerBufferProbe + { + public: + MuxerProbe(CameraBinImageCapture *capture) : capture(capture) {} + void probeCaps(GstCaps *caps); + bool probeBuffer(GstBuffer *buffer); + private: + CameraBinImageCapture * const capture; + + } m_muxerProbe; + + QVideoSurfaceFormat m_bufferFormat; + QSize m_jpegResolution; CameraBinSession *m_session; - bool m_ready; - int m_requestId; GstElement *m_jpegEncoderElement; GstElement *m_metadataMuxerElement; +#if GST_CHECK_VERSION(1,0,0) + GstVideoInfo m_videoInfo; +#else + int m_bytesPerLine; +#endif + int m_requestId; + bool m_ready; }; QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp index 824f9964f..739364f89 100644 --- a/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp @@ -49,7 +49,6 @@ CameraBinImageEncoder::~CameraBinImageEncoder() QList CameraBinImageEncoder::supportedResolutions(const QImageEncoderSettings &, bool *continuous) const { - qDebug() << "CameraBinImageEncoder::supportedResolutions()"; if (continuous) *continuous = false; diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp index ebfb08741..811225f68 100644 --- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp @@ -34,7 +34,11 @@ #include "camerabinimageprocessing.h" #include "camerabinsession.h" -#include +#if GST_CHECK_VERSION(1,0,0) +# include +#else +# include +#endif QT_BEGIN_NAMESPACE @@ -126,7 +130,7 @@ bool CameraBinImageProcessing::setColorBalanceValue(const QString& channel, qrea QCameraImageProcessing::WhiteBalanceMode CameraBinImageProcessing::whiteBalanceMode() const { #ifdef HAVE_GST_PHOTOGRAPHY - GstWhiteBalanceMode wbMode; + GstPhotographyWhiteBalanceMode wbMode; gst_photography_get_white_balance_mode(m_session->photography(), &wbMode); return m_mappedWbValues[wbMode]; #else diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h index dcefcd048..2c6347f03 100644 --- a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h +++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h @@ -41,7 +41,10 @@ #include #ifdef HAVE_GST_PHOTOGRAPHY -#include +# include +# if !GST_CHECK_VERSION(1,0,0) +typedef GstWhiteBalanceMode GstPhotographyWhiteBalanceMode; +# endif #endif QT_BEGIN_NAMESPACE @@ -73,7 +76,7 @@ private: CameraBinSession *m_session; QMap m_values; #ifdef HAVE_GST_PHOTOGRAPHY - QMap m_mappedWbValues; + QMap m_mappedWbValues; #endif }; diff --git a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp index 514813539..bc1b260a8 100644 --- a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp @@ -126,7 +126,7 @@ static const QGStreamerMetaDataKeys *qt_gstreamerMetaDataKeys() metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumTitle, GST_TAG_ALBUM, QVariant::String)); metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::AlbumArtist, GST_TAG_ARTIST, QVariant::String)); metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::ContributingArtist, GST_TAG_PERFORMER, QVariant::String)); -#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) +#if GST_CHECK_VERSION(0,10,19) metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Composer, GST_TAG_COMPOSER, QVariant::String)); #endif //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Conductor, 0, QVariant::String)); @@ -153,8 +153,7 @@ static const QGStreamerMetaDataKeys *qt_gstreamerMetaDataKeys() //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Director, 0, QVariant::String)); metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::LeadPerformer, GST_TAG_PERFORMER, QVariant::String)); //metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::Writer, 0, QVariant::String)); - -#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 30) +#if GST_CHECK_VERSION(0,10,30) // Photos metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraManufacturer, GST_TAG_DEVICE_MANUFACTURER, QVariant::String)); metadataKeys->append(QGStreamerMetaDataKey(QMediaMetaData::CameraModel, GST_TAG_DEVICE_MODEL, QVariant::String)); diff --git a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp index 3a04c2f47..801c7ab4e 100644 --- a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp @@ -110,9 +110,10 @@ void CameraBinRecorder::updateStatus() m_state = QMediaRecorder::StoppedState; m_session->stopVideoRecording(); } - m_status = m_session->pendingState() == QCamera::ActiveState ? - QMediaRecorder::LoadingStatus : - QMediaRecorder::UnloadedStatus; + m_status = m_session->pendingState() == QCamera::ActiveState + && m_session->captureMode().testFlag(QCamera::CaptureVideo) + ? QMediaRecorder::LoadingStatus + : QMediaRecorder::UnloadedStatus; } if (m_state != oldState) @@ -161,8 +162,6 @@ void CameraBinRecorder::applySettings() QVideoEncoderSettings videoSettings = videoEncoderControl->videoSettings(); videoSettings.setCodec(candidate[1]); - if (videoSettings.resolution().isEmpty()) - videoSettings.setResolution(640, 480); videoEncoderControl->setActualVideoSettings(videoSettings); QAudioEncoderSettings audioSettings = audioEncoderControl->audioSettings(); diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.cpp b/src/plugins/gstreamer/camerabin/camerabinservice.cpp index 969955f8a..388f2fdd5 100644 --- a/src/plugins/gstreamer/camerabin/camerabinservice.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinservice.cpp @@ -56,11 +56,11 @@ #include "camerabincapturedestination.h" #include "camerabinviewfindersettings.h" #include +#include #include #include - #if defined(HAVE_WIDGETS) #include #endif @@ -121,7 +121,6 @@ CameraBinService::CameraBinService(GstElementFactory *sourceFactory, QObject *pa #else m_videoWindow = new QGstreamerVideoWindow(this); #endif - #if defined(HAVE_WIDGETS) m_videoWidgetControl = new QGstreamerVideoWidgetControl(this); #endif @@ -150,8 +149,6 @@ QMediaControl *CameraBinService::requestControl(const char *name) if (!m_captureSession) return 0; - //qDebug() << "Request control" << name; - if (!m_videoOutput) { if (qstrcmp(name, QVideoRendererControl_iid) == 0) { m_videoOutput = m_videoRenderer; @@ -249,7 +246,7 @@ void CameraBinService::releaseControl(QMediaControl *control) bool CameraBinService::isCameraBinAvailable() { - GstElementFactory *factory = gst_element_factory_find("camerabin2"); + GstElementFactory *factory = gst_element_factory_find(QT_GSTREAMER_CAMERABIN_ELEMENT_NAME); if (factory) { gst_object_unref(GST_OBJECT(factory)); return true; diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index a4038c589..f916b58c7 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -140,8 +140,8 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa { if (m_sourceFactory) gst_object_ref(GST_OBJECT(m_sourceFactory)); + m_camerabin = gst_element_factory_make(QT_GSTREAMER_CAMERABIN_ELEMENT_NAME, "camerabin"); - m_camerabin = gst_element_factory_make("camerabin2", "camerabin2"); g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this); g_signal_connect(G_OBJECT(m_camerabin), "element-added", G_CALLBACK(elementAdded), this); g_signal_connect(G_OBJECT(m_camerabin), "element-removed", G_CALLBACK(elementRemoved), this); @@ -178,7 +178,15 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa //post image preview in RGB format g_object_set(G_OBJECT(m_camerabin), POST_PREVIEWS_PROPERTY, TRUE, NULL); +#if GST_CHECK_VERSION(1,0,0) + GstCaps *previewCaps = gst_caps_new_simple( + "video/x-raw", + "format", G_TYPE_STRING, "RGBx", + NULL); +#else GstCaps *previewCaps = gst_caps_from_string("video/x-raw-rgb"); +#endif + g_object_set(G_OBJECT(m_camerabin), PREVIEW_CAPS_PROPERTY, previewCaps, NULL); gst_caps_unref(previewCaps); } @@ -243,6 +251,7 @@ bool CameraBinSession::setupCameraBin() qWarning() << "Staring camera without viewfinder available"; m_viewfinderElement = gst_element_factory_make("fakesink", NULL); } + g_object_set(G_OBJECT(m_viewfinderElement), "sync", FALSE, NULL); qt_gst_object_ref_sink(GST_OBJECT(m_viewfinderElement)); gst_element_set_state(m_camerabin, GST_STATE_NULL); g_object_set(G_OBJECT(m_camerabin), VIEWFINDER_SINK_PROPERTY, m_viewfinderElement, NULL); @@ -251,61 +260,27 @@ bool CameraBinSession::setupCameraBin() return true; } -static GstCaps *resolutionToCaps(const QSize &resolution, const QPair &rate = qMakePair(0,0)) +static GstCaps *resolutionToCaps(const QSize &resolution, qreal frameRate = 0.0) { - if (resolution.isEmpty()) - return gst_caps_new_any(); + GstCaps *caps = QGstUtils::videoFilterCaps(); - GstCaps *caps = 0; - if (rate.second > 0) { - caps = gst_caps_new_full(gst_structure_new("video/x-raw-yuv", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - "framerate", GST_TYPE_FRACTION, rate.first, rate.second, - NULL), - gst_structure_new("video/x-raw-rgb", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - "framerate", GST_TYPE_FRACTION, rate.first, rate.second, - NULL), - gst_structure_new("video/x-raw-data", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - "framerate", GST_TYPE_FRACTION, rate.first, rate.second, - NULL), - gst_structure_new("video/x-android-buffer", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - "framerate", GST_TYPE_FRACTION, rate.first, rate.second, - NULL), - gst_structure_new("image/jpeg", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - "framerate", GST_TYPE_FRACTION, rate.first, rate.second, - NULL), - NULL); - } else { - caps = gst_caps_new_full (gst_structure_new ("video/x-raw-yuv", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - NULL), - gst_structure_new ("video/x-raw-rgb", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - NULL), - gst_structure_new("video/x-raw-data", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - NULL), - gst_structure_new ("video/x-android-buffer", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - NULL), - gst_structure_new ("image/jpeg", - "width", G_TYPE_INT, resolution.width(), - "height", G_TYPE_INT, resolution.height(), - NULL), - NULL); + if (!resolution.isEmpty()) { + gst_caps_set_simple( + caps, + "width", G_TYPE_INT, resolution.width(), + "height", G_TYPE_INT, resolution.height(), + NULL); + } + + if (frameRate > 0.0) { + gint numerator; + gint denominator; + gst_util_double_to_fraction(frameRate, &numerator, &denominator); + + gst_caps_set_simple( + caps, + "framerate", GST_TYPE_FRACTION, numerator, denominator, + NULL); } return caps; @@ -314,40 +289,40 @@ static GstCaps *resolutionToCaps(const QSize &resolution, const QPair void CameraBinSession::setupCaptureResolution() { QSize resolution = m_imageEncodeControl->imageSettings().resolution(); - if (!resolution.isEmpty()) { + { GstCaps *caps = resolutionToCaps(resolution); #if CAMERABIN_DEBUG - qDebug() << Q_FUNC_INFO << "set image resolution" << resolution << gst_caps_to_string(caps); + qDebug() << Q_FUNC_INFO << "set image resolution" << resolution << caps; #endif g_object_set(m_camerabin, IMAGE_CAPTURE_CAPS_PROPERTY, caps, NULL); - gst_caps_unref(caps); - } else { - g_object_set(m_camerabin, IMAGE_CAPTURE_CAPS_PROPERTY, NULL, NULL); + if (caps) + gst_caps_unref(caps); } + const QSize viewfinderResolution = m_viewfinderSettingsControl->resolution(); resolution = m_videoEncodeControl->actualVideoSettings().resolution(); - //qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); - if (!resolution.isEmpty()) { - GstCaps *caps = resolutionToCaps(resolution /*, framerate*/); //convert to rational + qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); + { + GstCaps *caps = resolutionToCaps( + !resolution.isEmpty() ? resolution : viewfinderResolution, framerate); #if CAMERABIN_DEBUG - qDebug() << Q_FUNC_INFO << "set video resolution" << resolution << gst_caps_to_string(caps); + qDebug() << Q_FUNC_INFO << "set video resolution" << resolution << caps; #endif g_object_set(m_camerabin, VIDEO_CAPTURE_CAPS_PROPERTY, caps, NULL); - gst_caps_unref(caps); - } else { - g_object_set(m_camerabin, VIDEO_CAPTURE_CAPS_PROPERTY, NULL, NULL); + if (caps) + gst_caps_unref(caps); } - resolution = m_viewfinderSettingsControl->resolution(); - if (!resolution.isEmpty()) { + if (!viewfinderResolution.isEmpty()) + resolution = viewfinderResolution; + { GstCaps *caps = resolutionToCaps(resolution); #if CAMERABIN_DEBUG - qDebug() << Q_FUNC_INFO << "set viewfinder resolution" << resolution << gst_caps_to_string(caps); + qDebug() << Q_FUNC_INFO << "set viewfinder resolution" << resolution << caps; #endif g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, caps, NULL); - gst_caps_unref(caps); - } else { - g_object_set(m_camerabin, VIEWFINDER_CAPS_PROPERTY, NULL, NULL); + if (caps) + gst_caps_unref(caps); } if (m_videoEncoder) @@ -363,13 +338,17 @@ void CameraBinSession::setAudioCaptureCaps() if (sampleRate == -1 && channelCount == -1) return; +#if GST_CHECK_VERSION(1,0,0) + GstStructure *structure = gst_structure_new_empty(QT_GSTREAMER_RAW_AUDIO_MIME); +#else GstStructure *structure = gst_structure_new( - "audio/x-raw-int", + QT_GSTREAMER_RAW_AUDIO_MIME, "endianness", G_TYPE_INT, 1234, "signed", G_TYPE_BOOLEAN, TRUE, "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL); +#endif if (sampleRate != -1) gst_structure_set(structure, "rate", G_TYPE_INT, sampleRate, NULL); if (channelCount != -1) @@ -760,7 +739,7 @@ qint64 CameraBinSession::duration() const if (fileSink) { GstFormat format = GST_FORMAT_TIME; gint64 duration = 0; - bool ret = gst_element_query_position(fileSink, &format, &duration); + bool ret = qt_gst_element_query_position(fileSink, format, &duration); gst_object_unref(GST_OBJECT(fileSink)); if (ret) return duration / 1000000; @@ -795,129 +774,57 @@ void CameraBinSession::setMetaData(const QMap &data) { m_metaData = data; - if (m_camerabin) { - GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_camerabin), GST_TYPE_TAG_SETTER); - GstElement *element = 0; - while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { - gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); - - QMapIterator it(data); - while (it.hasNext()) { - it.next(); - const QString tagName = it.key(); - const QVariant tagValue = it.value(); - - switch(tagValue.type()) { - case QVariant::String: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toString().toUtf8().constData(), - NULL); - break; - case QVariant::Int: - case QVariant::LongLong: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toInt(), - NULL); - break; - case QVariant::Double: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toDouble(), - NULL); - break; - case QVariant::DateTime: { - QDateTime date = tagValue.toDateTime().toLocalTime(); - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - gst_date_time_new_local_time( - date.date().year(), date.date().month(), date.date().day(), - date.time().hour(), date.time().minute(), date.time().second()), - NULL); - break; - } - default: - break; - } - } - } - gst_iterator_free(elements); - } + if (m_camerabin) + QGstUtils::setMetaData(m_camerabin, data); } bool CameraBinSession::processSyncMessage(const QGstreamerMessage &message) { GstMessage* gm = message.rawMessage(); - const GstStructure *st; - const GValue *image; - GstBuffer *buffer = NULL; if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) { - if (m_captureMode == QCamera::CaptureStillImage && - gst_structure_has_name(gm->structure, "preview-image")) { - st = gst_message_get_structure(gm); - - if (gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER)) { - image = gst_structure_get_value(st, "buffer"); - if (image) { - buffer = gst_value_get_buffer(image); - - QImage img; - - GstCaps *caps = gst_buffer_get_caps(buffer); - if (caps) { - GstStructure *structure = gst_caps_get_structure(caps, 0); - gint width = 0; - gint height = 0; -#if CAMERABIN_DEBUG - qDebug() << "Preview caps:" << gst_structure_to_string(structure); + const GstStructure *st = gst_message_get_structure(gm); + const GValue *sampleValue = 0; + if (m_captureMode == QCamera::CaptureStillImage + && gst_structure_has_name(st, "preview-image") +#if GST_CHECK_VERSION(1,0,0) + && gst_structure_has_field_typed(st, "sample", GST_TYPE_SAMPLE) + && (sampleValue = gst_structure_get_value(st, "sample"))) { + GstSample * const sample = gst_value_get_sample(sampleValue); + GstCaps * const previewCaps = gst_sample_get_caps(sample); + GstBuffer * const buffer = gst_sample_get_buffer(sample); +#else + && gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER) + && (sampleValue = gst_structure_get_value(st, "buffer"))) { + GstBuffer * const buffer = gst_value_get_buffer(sampleValue); #endif - if (structure && - gst_structure_get_int(structure, "width", &width) && - gst_structure_get_int(structure, "height", &height) && - width > 0 && height > 0) { - if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { - QImage::Format format = QImage::Format_Invalid; - int bpp = 0; - gst_structure_get_int(structure, "bpp", &bpp); - - if (bpp == 24) - format = QImage::Format_RGB888; - else if (bpp == 32) - format = QImage::Format_RGB32; - - if (format != QImage::Format_Invalid) { - img = QImage((const uchar *)buffer->data, width, height, format); - img.bits(); //detach - } - } - } - gst_caps_unref(caps); - - static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageExposed); - exposedSignal.invoke(this, - Qt::QueuedConnection, - Q_ARG(int,m_requestId)); - - static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageCaptured); - capturedSignal.invoke(this, - Qt::QueuedConnection, - Q_ARG(int,m_requestId), - Q_ARG(QImage,img)); - } - - } - return true; + QImage image; +#if GST_CHECK_VERSION(1,0,0) + GstVideoInfo previewInfo; + if (gst_video_info_from_caps(&previewInfo, previewCaps)) + image = QGstUtils::bufferToImage(buffer, previewInfo); + gst_sample_unref(sample); +#else + image = QGstUtils::bufferToImage(buffer); + gst_buffer_unref(buffer); +#endif + if (!image.isNull()) { + static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageExposed); + exposedSignal.invoke(this, + Qt::QueuedConnection, + Q_ARG(int,m_requestId)); + + static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&CameraBinSession::imageCaptured); + capturedSignal.invoke(this, + Qt::QueuedConnection, + Q_ARG(int,m_requestId), + Q_ARG(QImage,image)); } + return true; } #ifdef HAVE_GST_PHOTOGRAPHY - if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) + if (gst_structure_has_name(st, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) m_cameraFocusControl->handleFocusMessage(gm); #endif } @@ -1109,20 +1016,12 @@ QList< QPair > CameraBinSession::supportedFrameRates(const QSize &frame if (frameSize.isEmpty()) { caps = gst_caps_copy(supportedCaps); } else { - GstCaps *filter = gst_caps_new_full( - gst_structure_new( - "video/x-raw-rgb", - "width" , G_TYPE_INT , frameSize.width(), - "height" , G_TYPE_INT, frameSize.height(), NULL), - gst_structure_new( - "video/x-raw-yuv", - "width" , G_TYPE_INT, frameSize.width(), - "height" , G_TYPE_INT, frameSize.height(), NULL), - gst_structure_new( - "image/jpeg", - "width" , G_TYPE_INT, frameSize.width(), - "height" , G_TYPE_INT, frameSize.height(), NULL), - NULL); + GstCaps *filter = QGstUtils::videoFilterCaps(); + gst_caps_set_simple( + filter, + "width", G_TYPE_INT, frameSize.width(), + "height", G_TYPE_INT, frameSize.height(), + NULL); caps = gst_caps_intersect(supportedCaps, filter); gst_caps_unref(filter); @@ -1133,7 +1032,7 @@ QList< QPair > CameraBinSession::supportedFrameRates(const QSize &frame caps = gst_caps_make_writable(caps); for (uint i=0; i > CameraBinSession::supportedFrameRates(const QSize &frame gst_structure_remove_all_fields(structure); gst_structure_set_value(structure, "framerate", &rate); } +#if GST_CHECK_VERSION(1,0,0) + caps = gst_caps_simplify(caps); +#else gst_caps_do_simplify(caps); - +#endif for (uint i=0; i > CameraBinSession::supportedFrameRates(const QSize &frame qSort(res.begin(), res.end(), rateLessThan); #if CAMERABIN_DEBUG - qDebug() << "Supported rates:" << gst_caps_to_string(caps); + qDebug() << "Supported rates:" << caps; qDebug() << res; #endif @@ -1213,31 +1115,24 @@ QList CameraBinSession::supportedResolutions(QPair rate, SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY : SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY, &supportedCaps, NULL); - if (!supportedCaps) - return res; - #if CAMERABIN_DEBUG - qDebug() << "Source caps:" << gst_caps_to_string(supportedCaps); + qDebug() << "Source caps:" << supportedCaps; #endif + if (!supportedCaps) + return res; + GstCaps *caps = 0; bool isContinuous = false; if (rate.first <= 0 || rate.second <= 0) { caps = gst_caps_copy(supportedCaps); } else { - GstCaps *filter = gst_caps_new_full( - gst_structure_new( - "video/x-raw-rgb", - "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), - gst_structure_new( - "video/x-raw-yuv", - "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), - gst_structure_new( - "image/jpeg", - "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), - NULL); - + GstCaps *filter = QGstUtils::videoFilterCaps(); + gst_caps_set_simple( + filter, + "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, + NULL); caps = gst_caps_intersect(supportedCaps, filter); gst_caps_unref(filter); } @@ -1247,7 +1142,7 @@ QList CameraBinSession::supportedResolutions(QPair rate, caps = gst_caps_make_writable(caps); for (uint i=0; i CameraBinSession::supportedResolutions(QPair rate, gst_structure_set_value(structure, "width", &w); gst_structure_set_value(structure, "height", &h); } + +#if GST_CHECK_VERSION(1,0,0) + caps = gst_caps_simplify(caps); +#else gst_caps_do_simplify(caps); +#endif + for (uint i=0; i #include @@ -175,7 +176,7 @@ GstElement *QGstreamerAudioEncode::createEncoder() if (m_audioSettings.sampleRate() > 0 || m_audioSettings.channelCount() > 0) { GstCaps *caps = gst_caps_new_empty(); - GstStructure *structure = gst_structure_new("audio/x-raw-int", NULL); + GstStructure *structure = qt_gst_structure_new_empty(QT_GSTREAMER_RAW_AUDIO_MIME); if (m_audioSettings.sampleRate() > 0) gst_structure_set(structure, "rate", G_TYPE_INT, m_audioSettings.sampleRate(), NULL ); diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp index 97a165dca..1ab98cd4a 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp @@ -62,27 +62,25 @@ QT_BEGIN_NAMESPACE -QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObject *parent): - QMediaService(parent) -{ - m_captureSession = 0; - m_cameraControl = 0; - m_metaDataControl = 0; - +QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObject *parent) + : QMediaService(parent) + , m_captureSession(0) + , m_cameraControl(0) #if defined(USE_GSTREAMER_CAMERA) - m_videoInput = 0; + , m_videoInput(0) #endif - m_audioInputSelector = 0; - m_videoInputDevice = 0; - - m_videoOutput = 0; - m_videoRenderer = 0; - m_videoWindow = 0; + , m_metaDataControl(0) + , m_audioInputSelector(0) + , m_videoInputDevice(0) + , m_videoOutput(0) + , m_videoRenderer(0) + , m_videoWindow(0) #if defined(HAVE_WIDGETS) - m_videoWidgetControl = 0; + , m_videoWidgetControl(0) #endif - m_imageCaptureControl = 0; - + , m_imageCaptureControl(0) + , m_audioProbeControl(0) +{ if (service == Q_MEDIASERVICE_AUDIOSOURCE) { m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this); } @@ -163,12 +161,12 @@ QMediaControl *QGstreamerCaptureService::requestControl(const char *name) return m_imageCaptureControl; if (qstrcmp(name,QMediaAudioProbeControl_iid) == 0) { - if (m_captureSession) { - QGstreamerAudioProbeControl *probe = new QGstreamerAudioProbeControl(this); - m_captureSession->addProbe(probe); - return probe; + if (!m_audioProbeControl) { + m_audioProbeControl = new QGstreamerAudioProbeControl(this); + m_captureSession->addProbe(m_audioProbeControl); } - return 0; + m_audioProbeControl->ref.ref(); + return m_audioProbeControl; } if (!m_videoOutput) { @@ -194,17 +192,15 @@ QMediaControl *QGstreamerCaptureService::requestControl(const char *name) void QGstreamerCaptureService::releaseControl(QMediaControl *control) { - if (control && control == m_videoOutput) { + if (!control) { + return; + } else if (control == m_videoOutput) { m_videoOutput = 0; m_captureSession->setVideoPreview(0); - } - - QGstreamerAudioProbeControl* audioProbe = qobject_cast(control); - if (audioProbe) { - if (m_captureSession) - m_captureSession->removeProbe(audioProbe); - delete audioProbe; - return; + } else if (control == m_audioProbeControl && !m_audioProbeControl->ref.deref()) { + m_captureSession->removeProbe(m_audioProbeControl); + delete m_audioProbeControl; + m_audioProbeControl = 0; } } diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h index 7ff8ce253..e0cf4ee42 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h @@ -43,6 +43,7 @@ QT_BEGIN_NAMESPACE class QAudioInputSelectorControl; class QVideoDeviceSelectorControl; +class QGstreamerAudioProbeControl; class QGstreamerCaptureSession; class QGstreamerCameraControl; class QGstreamerMessage; @@ -86,6 +87,8 @@ private: QMediaControl *m_videoWidgetControl; #endif QGstreamerImageCaptureControl *m_imageCaptureControl; + + QGstreamerAudioProbeControl *m_audioProbeControl; }; QT_END_NAMESPACE diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp index 0ac34ee72..85ed687d3 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureserviceplugin.cpp @@ -110,90 +110,16 @@ QMultimedia::SupportEstimate QGstreamerCaptureServicePlugin::hasSupport(const QS return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet); } + +static bool isEncoderOrMuxer(GstElementFactory *factory) +{ + return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_MUXER) + || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_ENCODER); +} + void QGstreamerCaptureServicePlugin::updateSupportedMimeTypes() const { - //enumerate supported mime types - gst_init(NULL, NULL); - - GList *plugins, *orig_plugins; - orig_plugins = plugins = gst_default_registry_get_plugin_list (); - - while (plugins) { - GList *features, *orig_features; - - GstPlugin *plugin = (GstPlugin *) (plugins->data); - plugins = g_list_next (plugins); - - if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED - continue; - - orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get_default (), - plugin->desc.name); - while (features) { - if (!G_UNLIKELY(features->data == NULL)) { - GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); - if (GST_IS_ELEMENT_FACTORY (feature)) { - GstElementFactory *factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); - if (factory - && factory->numpadtemplates > 0 - && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0 - || qstrcmp(factory->details.klass, "Codec/Decoder/Video") == 0 - || qstrcmp(factory->details.klass, "Codec/Demux") == 0 )) { - const GList *pads = factory->staticpadtemplates; - while (pads) { - GstStaticPadTemplate *padtemplate = (GstStaticPadTemplate*)(pads->data); - pads = g_list_next (pads); - if (padtemplate->direction != GST_PAD_SINK) - continue; - if (padtemplate->static_caps.string) { - GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); - if (!gst_caps_is_any (caps) && ! gst_caps_is_empty (caps)) { - for (guint i = 0; i < gst_caps_get_size(caps); i++) { - GstStructure *structure = gst_caps_get_structure(caps, i); - QString nameLowcase = QString(gst_structure_get_name (structure)).toLower(); - - m_supportedMimeTypeSet.insert(nameLowcase); - if (nameLowcase.contains("mpeg")) { - //Because mpeg version number is only included in the detail - //description, it is necessary to manually extract this information - //in order to match the mime type of mpeg4. - const GValue *value = gst_structure_get_value(structure, "mpegversion"); - if (value) { - gchar *str = gst_value_serialize (value); - QString versions(str); - QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); - foreach (const QString &e, elements) - m_supportedMimeTypeSet.insert(nameLowcase + e); - g_free (str); - } - } - } - } - gst_caps_unref(caps); - } - } - gst_object_unref (factory); - } - } else if (GST_IS_TYPE_FIND_FACTORY(feature)) { - QString name(gst_plugin_feature_get_name(feature)); - if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type - m_supportedMimeTypeSet.insert(name.toLower()); - } - } - features = g_list_next (features); - } - gst_plugin_feature_list_free (orig_features); - } - gst_plugin_list_free (orig_plugins); - -#if defined QT_SUPPORTEDMIMETYPES_DEBUG - QStringList list = m_supportedMimeTypeSet.toList(); - list.sort(); - if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { - foreach (const QString &type, list) - qDebug() << type; - } -#endif + m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isEncoderOrMuxer); } QStringList QGstreamerCaptureServicePlugin::supportedMimeTypes() const diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp index a2bd80de8..af5b339e5 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp +++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -52,7 +53,6 @@ #include #include #include - #include QT_BEGIN_NAMESPACE @@ -64,7 +64,7 @@ QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::Cap m_waitingForEos(false), m_pipelineMode(EmptyPipeline), m_captureMode(captureMode), - m_audioBufferProbeId(-1), + m_audioProbe(0), m_audioInputFactory(0), m_audioPreviewFactory(0), m_videoInputFactory(0), @@ -169,7 +169,7 @@ GstElement *QGstreamerCaptureSession::buildEncodeBin() if (m_captureMode & Video) { GstElement *videoQueue = gst_element_factory_make("queue", "video-encode-queue"); - GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-encoder"); + GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-encoder"); GstElement *videoscale = gst_element_factory_make("videoscale","videoscale-encoder"); gst_bin_add_many(GST_BIN(encodeBin), videoQueue, colorspace, videoscale, NULL); @@ -280,7 +280,7 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() if (m_viewfinderInterface) { GstElement *bin = gst_bin_new("video-preview-bin"); - GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview"); + GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-preview"); GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video-preview"); GstElement *preview = m_viewfinderInterface->videoSink(); @@ -299,36 +299,25 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() resolution = m_imageEncodeControl->imageSettings().resolution(); } - if (!resolution.isEmpty() || frameRate > 0.001) { - GstCaps *caps = gst_caps_new_empty(); - QStringList structureTypes; - structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb"; - - foreach(const QString &structureType, structureTypes) { - GstStructure *structure = gst_structure_new(structureType.toLatin1().constData(), NULL); - - if (!resolution.isEmpty()) { - gst_structure_set(structure, "width", G_TYPE_INT, resolution.width(), NULL); - gst_structure_set(structure, "height", G_TYPE_INT, resolution.height(), NULL); - } - - if (frameRate > 0.001) { - QPair rate = m_videoEncodeControl->rateAsRational(); + GstCaps *caps = QGstUtils::videoFilterCaps(); - //qDebug() << "frame rate:" << num << denum; + if (!resolution.isEmpty()) { + gst_caps_set_simple(caps, "width", G_TYPE_INT, resolution.width(), NULL); + gst_caps_set_simple(caps, "height", G_TYPE_INT, resolution.height(), NULL); + } + if (frameRate > 0.001) { + QPair rate = m_videoEncodeControl->rateAsRational(); - gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); - } + //qDebug() << "frame rate:" << num << denum; - gst_caps_append_structure(caps,structure); - } + gst_caps_set_simple(caps, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); + } - //qDebug() << "set video preview caps filter:" << gst_caps_to_string(caps); + //qDebug() << "set video preview caps filter:" << gst_caps_to_string(caps); - g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); + g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); - gst_caps_unref(caps); - } + gst_caps_unref(caps); // add ghostpads GstPad *pad = gst_element_get_static_pad(colorspace, "sink"); @@ -342,7 +331,7 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() previewElement = gst_element_factory_make("fakesink", "video-preview"); #else GstElement *bin = gst_bin_new("video-preview-bin"); - GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview"); + GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-preview"); GstElement *preview = gst_element_factory_make("ximagesink", "video-preview"); gst_bin_add_many(GST_BIN(bin), colorspace, preview, NULL); gst_element_link(colorspace,preview); @@ -360,101 +349,49 @@ GstElement *QGstreamerCaptureSession::buildVideoPreview() return previewElement; } - -static gboolean passImageFilter(GstElement *element, - GstBuffer *buffer, - void *appdata) +void QGstreamerCaptureSession::probeCaps(GstCaps *caps) { - Q_UNUSED(element); - Q_UNUSED(buffer); - - QGstreamerCaptureSession *session = (QGstreamerCaptureSession *)appdata; - if (session->m_passImage || session->m_passPrerollImage) { - session->m_passImage = false; - - if (session->m_passPrerollImage) { - session->m_passPrerollImage = false; - return TRUE; - } - session->m_passPrerollImage = false; - - QImage img; - - GstCaps *caps = gst_buffer_get_caps(buffer); - if (caps) { - GstStructure *structure = gst_caps_get_structure (caps, 0); - gint width = 0; - gint height = 0; - - if (structure && - gst_structure_get_int(structure, "width", &width) && - gst_structure_get_int(structure, "height", &height) && - width > 0 && height > 0) { - if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { - guint32 fourcc = 0; - gst_structure_get_fourcc(structure, "format", &fourcc); - - if (fourcc == GST_MAKE_FOURCC('I','4','2','0')) { - img = QImage(width/2, height/2, QImage::Format_RGB32); - - const uchar *data = (const uchar *)buffer->data; +#if GST_CHECK_VERSION(1,0,0) + gst_video_info_from_caps(&m_previewInfo, caps); +#else + Q_UNUSED(caps); +#endif +} - for (int y=0; ydata, - width, - height, - format); - img.bits(); //detach - } - } - } - gst_caps_unref(caps); - } + if (img.isNull()) + return true; - static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageExposed); - exposedSignal.invoke(session, - Qt::QueuedConnection, - Q_ARG(int,session->m_imageRequestId)); + static QMetaMethod exposedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageExposed); + exposedSignal.invoke(this, + Qt::QueuedConnection, + Q_ARG(int,m_imageRequestId)); - static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageCaptured); - capturedSignal.invoke(session, - Qt::QueuedConnection, - Q_ARG(int,session->m_imageRequestId), - Q_ARG(QImage,img)); + static QMetaMethod capturedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageCaptured); + capturedSignal.invoke(this, + Qt::QueuedConnection, + Q_ARG(int,m_imageRequestId), + Q_ARG(QImage,img)); - return TRUE; - } else { - return FALSE; - } + return true; } static gboolean saveImageFilter(GstElement *element, @@ -471,7 +408,15 @@ static gboolean saveImageFilter(GstElement *element, if (!fileName.isEmpty()) { QFile f(fileName); if (f.open(QFile::WriteOnly)) { - f.write((const char *)buffer->data, buffer->size); +#if GST_CHECK_VERSION(1,0,0) + GstMapInfo info; + if (gst_buffer_map(buffer, &info, GST_MAP_READ)) { + f.write(reinterpret_cast(info.data), info.size); + gst_buffer_unmap(buffer, &info); + } +#else + f.write(reinterpret_cast(buffer->data), buffer->size); +#endif f.close(); static QMetaMethod savedSignal = QMetaMethod::fromSignal(&QGstreamerCaptureSession::imageSaved); @@ -489,18 +434,19 @@ GstElement *QGstreamerCaptureSession::buildImageCapture() { GstElement *bin = gst_bin_new("image-capture-bin"); GstElement *queue = gst_element_factory_make("queue", "queue-image-capture"); - GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-image-capture"); + GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "videoconvert-image-capture"); GstElement *encoder = gst_element_factory_make("jpegenc", "image-encoder"); GstElement *sink = gst_element_factory_make("fakesink","sink-image-capture"); GstPad *pad = gst_element_get_static_pad(queue, "src"); Q_ASSERT(pad); - gst_pad_add_buffer_probe(pad, G_CALLBACK(passImageFilter), this); + + addProbeToPad(pad, false); + gst_object_unref(GST_OBJECT(pad)); g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, NULL); - g_signal_connect(G_OBJECT(sink), "handoff", - G_CALLBACK(saveImageFilter), this); + g_signal_connect(G_OBJECT(sink), "handoff", G_CALLBACK(saveImageFilter), this); gst_bin_add_many(GST_BIN(bin), queue, colorspace, encoder, sink, NULL); gst_element_link_many(queue, colorspace, encoder, sink, NULL); @@ -715,6 +661,8 @@ void QGstreamerCaptureSession::dumpGraph(const QString &fileName) _gst_debug_bin_to_dot_file(GST_BIN(m_pipeline), GstDebugGraphDetails(/*GST_DEBUG_GRAPH_SHOW_ALL |*/ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES), fileName.toLatin1()); +#else + Q_UNUSED(fileName); #endif } @@ -877,10 +825,8 @@ void QGstreamerCaptureSession::setState(QGstreamerCaptureSession::State newState qint64 QGstreamerCaptureSession::duration() const { - GstFormat format = GST_FORMAT_TIME; - gint64 duration = 0; - - if ( m_encodeBin && gst_element_query_position(m_encodeBin, &format, &duration)) + gint64 duration = 0; + if (m_encodeBin && qt_gst_element_query_position(m_encodeBin, GST_FORMAT_TIME, &duration)) return duration / 1000000; else return 0; @@ -896,50 +842,8 @@ void QGstreamerCaptureSession::setMetaData(const QMap &dat //qDebug() << "QGstreamerCaptureSession::setMetaData" << data; m_metaData = data; - if (m_encodeBin) { - GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_encodeBin), GST_TYPE_TAG_SETTER); - GstElement *element = 0; - while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { - //qDebug() << "found element with tag setter interface:" << gst_element_get_name(element); - QMapIterator it(data); - while (it.hasNext()) { - it.next(); - const QString tagName = it.key(); - const QVariant tagValue = it.value(); - - - switch(tagValue.type()) { - case QVariant::String: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE_ALL, - tagName.toUtf8().constData(), - tagValue.toString().toUtf8().constData(), - NULL); - break; - case QVariant::Int: - case QVariant::LongLong: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE_ALL, - tagName.toUtf8().constData(), - tagValue.toInt(), - NULL); - break; - case QVariant::Double: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE_ALL, - tagName.toUtf8().constData(), - tagValue.toDouble(), - NULL); - break; - default: - break; - } - - } - - } - gst_iterator_free(elements); - } + if (m_encodeBin) + QGstUtils::setMetaData(GST_BIN(m_encodeBin), data); } bool QGstreamerCaptureSession::processBusMessage(const QGstreamerMessage &message) @@ -1058,34 +962,16 @@ void QGstreamerCaptureSession::setVolume(qreal volume) void QGstreamerCaptureSession::addProbe(QGstreamerAudioProbeControl* probe) { - QMutexLocker locker(&m_audioProbeMutex); - - if (m_audioProbes.contains(probe)) - return; - - m_audioProbes.append(probe); + Q_ASSERT(!m_audioProbe); + m_audioProbe = probe; + addAudioBufferProbe(); } void QGstreamerCaptureSession::removeProbe(QGstreamerAudioProbeControl* probe) { - QMutexLocker locker(&m_audioProbeMutex); - m_audioProbes.removeOne(probe); -} - -gboolean QGstreamerCaptureSession::padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) -{ - Q_UNUSED(pad); - - QGstreamerCaptureSession *session = reinterpret_cast(user_data); - QMutexLocker locker(&session->m_audioProbeMutex); - - if (session->m_audioProbes.isEmpty()) - return TRUE; - - foreach (QGstreamerAudioProbeControl* probe, session->m_audioProbes) - probe->bufferProbed(buffer); - - return TRUE; + Q_ASSERT(m_audioProbe == probe); + removeAudioBufferProbe(); + m_audioProbe = 0; } GstPad *QGstreamerCaptureSession::getAudioProbePad() @@ -1114,26 +1000,25 @@ GstPad *QGstreamerCaptureSession::getAudioProbePad() void QGstreamerCaptureSession::removeAudioBufferProbe() { - if (m_audioBufferProbeId == -1) + if (!m_audioProbe) return; GstPad *pad = getAudioProbePad(); if (pad) { - gst_pad_remove_buffer_probe(pad, m_audioBufferProbeId); - gst_object_unref(G_OBJECT(pad)); + m_audioProbe->removeProbeFromPad(pad); + gst_object_unref(GST_OBJECT(pad)); } - - m_audioBufferProbeId = -1; } void QGstreamerCaptureSession::addAudioBufferProbe() { - Q_ASSERT(m_audioBufferProbeId == -1); + if (!m_audioProbe) + return; GstPad *pad = getAudioProbePad(); if (pad) { - m_audioBufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padAudioBufferProbe), this); - gst_object_unref(G_OBJECT(pad)); + m_audioProbe->addProbeToPad(pad); + gst_object_unref(GST_OBJECT(pad)); } } diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h index a759f22e5..ad26327e7 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h +++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h @@ -41,8 +41,10 @@ #include #include +#include #include +#include QT_BEGIN_NAMESPACE @@ -70,7 +72,10 @@ public: virtual QList supportedResolutions(qreal frameRate = -1) const = 0; }; -class QGstreamerCaptureSession : public QObject, public QGstreamerBusMessageFilter +class QGstreamerCaptureSession + : public QObject + , public QGstreamerBusMessageFilter + , private QGstreamerBufferProbe { Q_OBJECT Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) @@ -131,7 +136,6 @@ public: void addProbe(QGstreamerAudioProbeControl* probe); void removeProbe(QGstreamerAudioProbeControl* probe); - static gboolean padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); signals: void stateChanged(QGstreamerCaptureSession::State state); @@ -156,6 +160,9 @@ public slots: void setVolume(qreal volume); private: + void probeCaps(GstCaps *caps); + bool probeBuffer(GstBuffer *buffer); + enum PipelineMode { EmptyPipeline, PreviewPipeline, RecordingPipeline, PreviewAndRecordingPipeline }; GstElement *buildEncodeBin(); @@ -180,9 +187,7 @@ private: QGstreamerCaptureSession::CaptureMode m_captureMode; QMap m_metaData; - QList m_audioProbes; - QMutex m_audioProbeMutex; - int m_audioBufferProbeId; + QGstreamerAudioProbeControl *m_audioProbe; QGstreamerElementFactory *m_audioInputFactory; QGstreamerElementFactory *m_audioPreviewFactory; @@ -217,6 +222,10 @@ private: GstElement *m_encodeBin; +#if GST_CHECK_VERSION(1,0,0) + GstVideoInfo m_previewInfo; +#endif + public: bool m_passImage; bool m_passPrerollImage; diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp index 2f0d0ee76..81b85d7b0 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp +++ b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp @@ -34,7 +34,7 @@ #include "qgstreamervideoencode.h" #include "qgstreamercapturesession.h" #include "qgstreamermediacontainercontrol.h" - +#include #include #include @@ -147,7 +147,7 @@ GstElement *QGstreamerVideoEncode::createEncoder() GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video"); gst_bin_add(encoderBin, capsFilter); - GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", NULL); + GstElement *colorspace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, NULL); gst_bin_add(encoderBin, colorspace); gst_bin_add(encoderBin, encoderElement); @@ -252,27 +252,22 @@ GstElement *QGstreamerVideoEncode::createEncoder() } if (!m_videoSettings.resolution().isEmpty() || m_videoSettings.frameRate() > 0.001) { - GstCaps *caps = gst_caps_new_empty(); - QStringList structureTypes; - structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb"; - - foreach(const QString &structureType, structureTypes) { - GstStructure *structure = gst_structure_new(structureType.toLatin1().constData(), NULL); - - if (!m_videoSettings.resolution().isEmpty()) { - gst_structure_set(structure, "width", G_TYPE_INT, m_videoSettings.resolution().width(), NULL); - gst_structure_set(structure, "height", G_TYPE_INT, m_videoSettings.resolution().height(), NULL); - } - - if (m_videoSettings.frameRate() > 0.001) { - QPair rate = rateAsRational(); - - //qDebug() << "frame rate:" << num << denum; - - gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); - } + GstCaps *caps = QGstUtils::videoFilterCaps(); + + if (!m_videoSettings.resolution().isEmpty()) { + gst_caps_set_simple( + caps, + "width", G_TYPE_INT, m_videoSettings.resolution().width(), + "height", G_TYPE_INT, m_videoSettings.resolution().height(), + NULL); + } - gst_caps_append_structure(caps,structure); + if (m_videoSettings.frameRate() > 0.001) { + QPair rate = rateAsRational(); + gst_caps_set_simple( + caps, + "framerate", GST_TYPE_FRACTION, rate.first, rate.second, + NULL); } //qDebug() << "set video caps filter:" << gst_caps_to_string(caps); diff --git a/src/plugins/gstreamer/mediaplayer/mediaplayer.pro b/src/plugins/gstreamer/mediaplayer/mediaplayer.pro index 2ca9377db..b986fc787 100644 --- a/src/plugins/gstreamer/mediaplayer/mediaplayer.pro +++ b/src/plugins/gstreamer/mediaplayer/mediaplayer.pro @@ -28,4 +28,3 @@ SOURCES += \ OTHER_FILES += \ mediaplayer.json - diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp index fed756ac9..c1fb64acd 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp @@ -425,7 +425,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice * m_session->loadFromUri(request); #endif - #if defined(HAVE_GST_APPSRC) if (!request.url().isEmpty() || userStreamValid) { #else diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp index ce267d737..84805b67a 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp @@ -51,7 +51,11 @@ #include #if defined(Q_WS_MAEMO_6) && defined(__arm__) -#include "qgstreamergltexturerenderer.h" +#include "private/qgstreamergltexturerenderer.h" +#endif + +#if defined(HAVE_MIR) && defined (__arm__) +#include "private/qgstreamermirtexturerenderer_p.h" #endif #include "qgstreamerstreamscontrol.h" @@ -66,6 +70,8 @@ QT_BEGIN_NAMESPACE QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): QMediaService(parent) + , m_audioProbeControl(0) + , m_videoProbeControl(0) , m_videoOutput(0) , m_videoRenderer(0) , m_videoWindow(0) @@ -82,6 +88,8 @@ QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): #if defined(Q_WS_MAEMO_6) && defined(__arm__) m_videoRenderer = new QGstreamerGLTextureRenderer(this); +#elif defined(HAVE_MIR) && defined (__arm__) + m_videoRenderer = new QGstreamerMirTextureRenderer(this, m_session); #else m_videoRenderer = new QGstreamerVideoRenderer(this); #endif @@ -115,23 +123,23 @@ QMediaControl *QGstreamerPlayerService::requestControl(const char *name) if (qstrcmp(name, QMediaAvailabilityControl_iid) == 0) return m_availabilityControl; - if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) { - if (m_session) { - QGstreamerVideoProbeControl *probe = new QGstreamerVideoProbeControl(this); + if (qstrcmp(name, QMediaVideoProbeControl_iid) == 0) { + if (!m_videoProbeControl) { increaseVideoRef(); - m_session->addProbe(probe); - return probe; + m_videoProbeControl = new QGstreamerVideoProbeControl(this); + m_session->addProbe(m_videoProbeControl); } - return 0; + m_videoProbeControl->ref.ref(); + return m_videoProbeControl; } - if (qstrcmp(name,QMediaAudioProbeControl_iid) == 0) { - if (m_session) { - QGstreamerAudioProbeControl *probe = new QGstreamerAudioProbeControl(this); - m_session->addProbe(probe); - return probe; + if (qstrcmp(name, QMediaAudioProbeControl_iid) == 0) { + if (!m_audioProbeControl) { + m_audioProbeControl = new QGstreamerAudioProbeControl(this); + m_session->addProbe(m_audioProbeControl); } - return 0; + m_audioProbeControl->ref.ref(); + return m_audioProbeControl; } if (!m_videoOutput) { @@ -156,28 +164,21 @@ QMediaControl *QGstreamerPlayerService::requestControl(const char *name) void QGstreamerPlayerService::releaseControl(QMediaControl *control) { - if (control == m_videoOutput) { + if (!control) { + return; + } else if (control == m_videoOutput) { m_videoOutput = 0; m_control->setVideoOutput(0); decreaseVideoRef(); - } - - QGstreamerVideoProbeControl* videoProbe = qobject_cast(control); - if (videoProbe) { - if (m_session) { - m_session->removeProbe(videoProbe); - decreaseVideoRef(); - } - delete videoProbe; - return; - } - - QGstreamerAudioProbeControl* audioProbe = qobject_cast(control); - if (audioProbe) { - if (m_session) - m_session->removeProbe(audioProbe); - delete audioProbe; - return; + } else if (control == m_videoProbeControl && !m_videoProbeControl->ref.deref()) { + m_session->removeProbe(m_videoProbeControl); + delete m_videoProbeControl; + m_videoProbeControl = 0; + decreaseVideoRef(); + } else if (control == m_audioProbeControl && !m_audioProbeControl->ref.deref()) { + m_session->removeProbe(m_audioProbeControl); + delete m_audioProbeControl; + m_audioProbeControl = 0; } } diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h index f3081e986..22be262ab 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h @@ -52,6 +52,8 @@ class QGstreamerStreamsControl; class QGstreamerVideoRenderer; class QGstreamerVideoWidgetControl; class QGStreamerAvailabilityControl; +class QGstreamerAudioProbeControl; +class QGstreamerVideoProbeControl; class QGstreamerPlayerService : public QMediaService { @@ -70,6 +72,9 @@ private: QGstreamerStreamsControl *m_streamsControl; QGStreamerAvailabilityControl *m_availabilityControl; + QGstreamerAudioProbeControl *m_audioProbeControl; + QGstreamerVideoProbeControl *m_videoProbeControl; + QMediaControl *m_videoOutput; QMediaControl *m_videoRenderer; QMediaControl *m_videoWindow; diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp index 7d20b6d92..f1fd4211c 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerserviceplugin.cpp @@ -81,89 +81,15 @@ QMultimedia::SupportEstimate QGstreamerPlayerServicePlugin::hasSupport(const QSt return QGstUtils::hasSupport(mimeType, codecs, m_supportedMimeTypeSet); } -void QGstreamerPlayerServicePlugin::updateSupportedMimeTypes() const +static bool isDecoderOrDemuxer(GstElementFactory *factory) { - //enumerate supported mime types - gst_init(NULL, NULL); - - GList *plugins, *orig_plugins; - orig_plugins = plugins = gst_default_registry_get_plugin_list (); - - while (plugins) { - GList *features, *orig_features; - - GstPlugin *plugin = (GstPlugin *) (plugins->data); - plugins = g_list_next (plugins); - - if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED - continue; - - orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get_default (), - plugin->desc.name); - while (features) { - if (!G_UNLIKELY(features->data == NULL)) { - GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); - if (GST_IS_ELEMENT_FACTORY (feature)) { - GstElementFactory *factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); - if (factory - && factory->numpadtemplates > 0 - && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0 - || qstrcmp(factory->details.klass, "Codec/Decoder/Video") == 0 - || qstrcmp(factory->details.klass, "Codec/Demux") == 0 )) { - const GList *pads = factory->staticpadtemplates; - while (pads) { - GstStaticPadTemplate *padtemplate = (GstStaticPadTemplate*)(pads->data); - pads = g_list_next (pads); - if (padtemplate->direction != GST_PAD_SINK) - continue; - if (padtemplate->static_caps.string) { - GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); - if (!gst_caps_is_any (caps) && ! gst_caps_is_empty (caps)) { - for (guint i = 0; i < gst_caps_get_size(caps); i++) { - GstStructure *structure = gst_caps_get_structure(caps, i); - QString nameLowcase = QString(gst_structure_get_name (structure)).toLower(); - - m_supportedMimeTypeSet.insert(nameLowcase); - if (nameLowcase.contains("mpeg")) { - //Because mpeg version number is only included in the detail - //description, it is necessary to manually extract this information - //in order to match the mime type of mpeg4. - const GValue *value = gst_structure_get_value(structure, "mpegversion"); - if (value) { - gchar *str = gst_value_serialize (value); - QString versions(str); - QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); - foreach (const QString &e, elements) - m_supportedMimeTypeSet.insert(nameLowcase + e); - g_free (str); - } - } - } - } - } - } - gst_object_unref (factory); - } - } else if (GST_IS_TYPE_FIND_FACTORY(feature)) { - QString name(gst_plugin_feature_get_name(feature)); - if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type - m_supportedMimeTypeSet.insert(name.toLower()); - } - } - features = g_list_next (features); - } - gst_plugin_feature_list_free (orig_features); - } - gst_plugin_list_free (orig_plugins); + return gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DEMUXER) + || gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DECODER); +} -#if defined QT_SUPPORTEDMIMETYPES_DEBUG - QStringList list = m_supportedMimeTypeSet.toList(); - list.sort(); - if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { - foreach (const QString &type, list) - qDebug() << type; - } -#endif +void QGstreamerPlayerServicePlugin::updateSupportedMimeTypes() const +{ + m_supportedMimeTypeSet = QGstUtils::supportedMimeTypes(isDecoderOrDemuxer); } QStringList QGstreamerPlayerServicePlugin::supportedMimeTypes() const diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp index 15924a68b..b5c354dff 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp @@ -37,7 +37,9 @@ #include #include #include +#if !GST_CHECK_VERSION(1,0,0) #include +#endif #include #include #include @@ -85,6 +87,7 @@ typedef enum { GST_PLAY_FLAG_BUFFERING = 0x000000100 } GstPlayFlags; +#if !GST_CHECK_VERSION(1,0,0) #define DEFAULT_RAW_CAPS \ "video/x-raw-yuv; " \ "video/x-raw-rgb; " \ @@ -97,7 +100,9 @@ typedef enum { "text/x-pango-markup; " \ "video/x-dvd-subpicture; " \ "subpicture/x-pgs" + static GstStaticCaps static_RawCaps = GST_STATIC_CAPS(DEFAULT_RAW_CAPS); +#endif QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) :QObject(parent), @@ -105,7 +110,9 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) m_pendingState(QMediaPlayer::StoppedState), m_busHelper(0), m_playbin(0), +#if !GST_CHECK_VERSION(1,0,0) m_usingColorspaceElement(false), +#endif m_videoSink(0), m_pendingVideoSink(0), m_nullVideoSink(0), @@ -117,8 +124,8 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) #if defined(HAVE_GST_APPSRC) m_appSrc(0), #endif - m_videoBufferProbeId(-1), - m_audioBufferProbeId(-1), + m_videoProbe(0), + m_audioProbe(0), m_volume(100), m_playbackRate(1.0), m_muted(false), @@ -138,8 +145,7 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) Q_ASSERT(result == TRUE); Q_UNUSED(result); - m_playbin = gst_element_factory_make("playbin2", NULL); - + m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, NULL); if (m_playbin) { //GST_PLAY_FLAG_NATIVE_VIDEO omits configuration of ffmpegcolorspace and videoscale, //since those elements are included in the video output bin when necessary. @@ -147,13 +153,14 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) int flags = GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_NATIVE_AUDIO; #else - int flags = 0; - g_object_get(G_OBJECT(m_playbin), "flags", &flags, NULL); + int flags = GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO; QByteArray envFlags = qgetenv("QT_GSTREAMER_PLAYBIN_FLAGS"); if (!envFlags.isEmpty()) { flags |= envFlags.toInt(); +#if !GST_CHECK_VERSION(1,0,0) } else { flags |= GST_PLAY_FLAG_NATIVE_VIDEO; +#endif } #endif g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL); @@ -185,12 +192,16 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) } } +#if GST_CHECK_VERSION(1,0,0) + m_videoIdentity = gst_element_factory_make("identity", NULL); // floating ref +#else m_videoIdentity = GST_ELEMENT(g_object_new(gst_video_connector_get_type(), 0)); // floating ref g_signal_connect(G_OBJECT(m_videoIdentity), "connection-failed", G_CALLBACK(insertColorSpaceElement), (gpointer)this); + m_colorSpace = gst_element_factory_make(QT_GSTREAMER_COLORCONVERSION_ELEMENT_NAME, "ffmpegcolorspace-vo"); - m_colorSpace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-vo"); // might not get a parent, take ownership to avoid leak qt_gst_object_ref_sink(GST_OBJECT(m_colorSpace)); +#endif m_nullVideoSink = gst_element_factory_make("fakesink", NULL); g_object_set(G_OBJECT(m_nullVideoSink), "sync", true, NULL); @@ -206,7 +217,7 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) // add ghostpads GstPad *pad = gst_element_get_static_pad(m_videoIdentity,"sink"); - gst_element_add_pad(GST_ELEMENT(m_videoOutputBin), gst_ghost_pad_new("videosink", pad)); + gst_element_add_pad(GST_ELEMENT(m_videoOutputBin), gst_ghost_pad_new("sink", pad)); gst_object_unref(GST_OBJECT(pad)); if (m_playbin != 0) { @@ -244,7 +255,9 @@ QGstreamerPlayerSession::~QGstreamerPlayerSession() delete m_busHelper; gst_object_unref(GST_OBJECT(m_bus)); gst_object_unref(GST_OBJECT(m_playbin)); +#if !GST_CHECK_VERSION(1,0,0) gst_object_unref(GST_OBJECT(m_colorSpace)); +#endif gst_object_unref(GST_OBJECT(m_nullVideoSink)); gst_object_unref(GST_OBJECT(m_videoOutputBin)); } @@ -339,12 +352,10 @@ qint64 QGstreamerPlayerSession::duration() const qint64 QGstreamerPlayerSession::position() const { - GstFormat format = GST_FORMAT_TIME; gint64 position = 0; - if ( m_playbin && gst_element_query_position(m_playbin, &format, &position)) + if (m_playbin && qt_gst_element_query_position(m_playbin, GST_FORMAT_TIME, &position)) m_lastPosition = position / 1000000; - return m_lastPosition; } @@ -474,17 +485,26 @@ bool QGstreamerPlayerSession::isAudioAvailable() const return m_audioAvailable; } +#if GST_CHECK_VERSION(1,0,0) +static GstPadProbeReturn block_pad_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) +#else static void block_pad_cb(GstPad *pad, gboolean blocked, gpointer user_data) +#endif { Q_UNUSED(pad); +#if GST_CHECK_VERSION(1,0,0) + Q_UNUSED(info); + Q_UNUSED(user_data); + return GST_PAD_PROBE_OK; +#else #ifdef DEBUG_PLAYBIN qDebug() << "block_pad_cb, blocked:" << blocked; #endif - if (blocked && user_data) { QGstreamerPlayerSession *session = reinterpret_cast(user_data); QMetaObject::invokeMethod(session, "finishVideoOutputChange", Qt::QueuedConnection); } +#endif } void QGstreamerPlayerSession::updateVideoRenderer() @@ -529,7 +549,7 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) m_renderer = renderer; #ifdef DEBUG_VO_BIN_DUMP - _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), + gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/), "playbin_set"); #endif @@ -570,12 +590,14 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) gst_element_set_state(m_videoSink, GST_STATE_NULL); gst_element_set_state(m_playbin, GST_STATE_NULL); +#if !GST_CHECK_VERSION(1,0,0) if (m_usingColorspaceElement) { gst_element_unlink(m_colorSpace, m_videoSink); gst_bin_remove(GST_BIN(m_videoOutputBin), m_colorSpace); } else { gst_element_unlink(m_videoIdentity, m_videoSink); } +#endif removeVideoBufferProbe(); @@ -585,8 +607,9 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); - m_usingColorspaceElement = false; bool linked = gst_element_link(m_videoIdentity, m_videoSink); +#if !GST_CHECK_VERSION(1,0,0) + m_usingColorspaceElement = false; if (!linked) { m_usingColorspaceElement = true; #ifdef DEBUG_PLAYBIN @@ -595,6 +618,10 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) gst_bin_add(GST_BIN(m_videoOutputBin), m_colorSpace); linked = gst_element_link_many(m_videoIdentity, m_colorSpace, m_videoSink, NULL); } +#endif + + if (!linked) + qWarning() << "Linking video output element failed"; if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "show-preroll-frame") != 0) { gboolean value = m_displayPrerolledFrame; @@ -633,7 +660,11 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) //block pads, async to avoid locking in paused state GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); +#if GST_CHECK_VERSION(1,0,0) + this->pad_probe_id = gst_pad_add_probe(srcPad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BLOCKING), block_pad_cb, this, NULL); +#else gst_pad_set_blocked_async(srcPad, true, &block_pad_cb, this); +#endif gst_object_unref(GST_OBJECT(srcPad)); //Unpause the sink to avoid waiting until the buffer is processed @@ -671,16 +702,22 @@ void QGstreamerPlayerSession::finishVideoOutputChange() } if (m_pendingVideoSink == m_videoSink) { + qDebug() << "Abort, no change"; //video output was change back to the current one, //no need to torment the pipeline, just unblock the pad if (gst_pad_is_blocked(srcPad)) +#if GST_CHECK_VERSION(1,0,0) + gst_pad_remove_probe(srcPad, this->pad_probe_id); +#else gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); +#endif m_pendingVideoSink = 0; gst_object_unref(GST_OBJECT(srcPad)); return; } +#if !GST_CHECK_VERSION(1,0,0) if (m_usingColorspaceElement) { gst_element_set_state(m_colorSpace, GST_STATE_NULL); gst_element_set_state(m_videoSink, GST_STATE_NULL); @@ -688,6 +725,9 @@ void QGstreamerPlayerSession::finishVideoOutputChange() gst_element_unlink(m_colorSpace, m_videoSink); gst_bin_remove(GST_BIN(m_videoOutputBin), m_colorSpace); } else { +#else + { +#endif gst_element_set_state(m_videoSink, GST_STATE_NULL); gst_element_unlink(m_videoIdentity, m_videoSink); } @@ -703,8 +743,9 @@ void QGstreamerPlayerSession::finishVideoOutputChange() addVideoBufferProbe(); - m_usingColorspaceElement = false; bool linked = gst_element_link(m_videoIdentity, m_videoSink); +#if !GST_CHECK_VERSION(1,0,0) + m_usingColorspaceElement = false; if (!linked) { m_usingColorspaceElement = true; #ifdef DEBUG_PLAYBIN @@ -713,6 +754,7 @@ void QGstreamerPlayerSession::finishVideoOutputChange() gst_bin_add(GST_BIN(m_videoOutputBin), m_colorSpace); linked = gst_element_link_many(m_videoIdentity, m_colorSpace, m_videoSink, NULL); } +#endif if (!linked) qWarning() << "Linking video output element failed"; @@ -720,6 +762,8 @@ void QGstreamerPlayerSession::finishVideoOutputChange() #ifdef DEBUG_PLAYBIN qDebug() << "notify the video connector it has to emit a new segment message..."; #endif + +#if !GST_CHECK_VERSION(1,0,0) //it's necessary to send a new segment event just before //the first buffer pushed to the new sink g_signal_emit_by_name(m_videoIdentity, @@ -727,7 +771,7 @@ void QGstreamerPlayerSession::finishVideoOutputChange() true //emit connection-failed signal //to have a chance to insert colorspace element ); - +#endif GstState state = GST_STATE_VOID_PENDING; @@ -743,8 +787,10 @@ void QGstreamerPlayerSession::finishVideoOutputChange() break; } +#if !GST_CHECK_VERSION(1,0,0) if (m_usingColorspaceElement) gst_element_set_state(m_colorSpace, state); +#endif gst_element_set_state(m_videoSink, state); @@ -760,16 +806,23 @@ void QGstreamerPlayerSession::finishVideoOutputChange() //don't have to wait here, it will unblock eventually if (gst_pad_is_blocked(srcPad)) - gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); +#if GST_CHECK_VERSION(1,0,0) + gst_pad_remove_probe(srcPad, this->pad_probe_id); +#else + gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); +#endif + gst_object_unref(GST_OBJECT(srcPad)); #ifdef DEBUG_VO_BIN_DUMP - _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), - GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/), + gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), + GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* | GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES */), "playbin_finish"); #endif } +#if !GST_CHECK_VERSION(1,0,0) + void QGstreamerPlayerSession::insertColorSpaceElement(GstElement *element, gpointer data) { #ifdef DEBUG_PLAYBIN @@ -814,6 +867,7 @@ void QGstreamerPlayerSession::insertColorSpaceElement(GstElement *element, gpoin gst_element_set_state(session->m_colorSpace, state); } +#endif bool QGstreamerPlayerSession::isVideoAvailable() const { @@ -830,6 +884,7 @@ bool QGstreamerPlayerSession::play() #ifdef DEBUG_PLAYBIN qDebug() << Q_FUNC_INFO; #endif + m_everPlayed = false; if (m_playbin) { m_pendingState = QMediaPlayer::PlayingState; @@ -1161,21 +1216,20 @@ bool QGstreamerPlayerSession::processBusMessage(const QGstreamerMessage &message case GST_MESSAGE_SEGMENT_DONE: break; case GST_MESSAGE_LATENCY: -#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 13) +#if GST_CHECK_VERSION(0,10,13) case GST_MESSAGE_ASYNC_START: break; case GST_MESSAGE_ASYNC_DONE: { - GstFormat format = GST_FORMAT_TIME; gint64 position = 0; - if (gst_element_query_position(m_playbin, &format, &position)) { + if (qt_gst_element_query_position(m_playbin, GST_FORMAT_TIME, &position)) { position /= 1000000; m_lastPosition = position; emit positionChanged(position); } break; } -#if GST_VERSION_MICRO >= 23 +#if GST_CHECK_VERSION(0,10,23) case GST_MESSAGE_REQUEST_STATE: #endif #endif @@ -1327,8 +1381,11 @@ void QGstreamerPlayerSession::getStreamsInfo() default: break; } - +#if GST_CHECK_VERSION(1,0,0) + if (tags && GST_IS_TAG_LIST(tags)) { +#else if (tags && gst_is_tag_list(tags)) { +#endif gchar *languageCode = 0; if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) streamProperties[QMediaMetaData::Language] = QString::fromUtf8(languageCode); @@ -1365,9 +1422,8 @@ void QGstreamerPlayerSession::updateVideoResolutionTag() #endif QSize size; QSize aspectRatio; - GstPad *pad = gst_element_get_static_pad(m_videoIdentity, "src"); - GstCaps *caps = gst_pad_get_negotiated_caps(pad); + GstCaps *caps = qt_gst_pad_get_current_caps(pad); if (caps) { const GstStructure *structure = gst_caps_get_structure(caps, 0); @@ -1407,11 +1463,10 @@ void QGstreamerPlayerSession::updateVideoResolutionTag() void QGstreamerPlayerSession::updateDuration() { - GstFormat format = GST_FORMAT_TIME; gint64 gstDuration = 0; int duration = -1; - if (m_playbin && gst_element_query_duration(m_playbin, &format, &gstDuration)) + if (m_playbin && qt_gst_element_query_duration(m_playbin, GST_FORMAT_TIME, &gstDuration)) duration = gstDuration / 1000000; if (m_duration != duration) { @@ -1467,7 +1522,7 @@ void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpo // The rest if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "extra-headers") != 0) { - GstStructure *extras = gst_structure_empty_new("extras"); + GstStructure *extras = qt_gst_structure_new_empty("extras"); foreach (const QByteArray &rawHeader, self->m_request.rawHeaderList()) { if (rawHeader == userAgentString) // Filter User-Agent @@ -1528,7 +1583,8 @@ void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpo qDebug() << "Current source is a non-live source"; #endif - g_object_set(G_OBJECT(self->m_videoSink), "sync", !self->m_isLiveSource, NULL); + if (self->m_videoSink) + g_object_set(G_OBJECT(self->m_videoSink), "sync", !self->m_isLiveSource, NULL); gst_object_unref(source); } @@ -1623,7 +1679,11 @@ GstAutoplugSelectResult QGstreamerPlayerSession::handleAutoplugSelect(GstBin *bi const gchar *factoryName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); if (g_str_has_prefix(factoryName, "vaapi")) { GstPad *sinkPad = gst_element_get_static_pad(session->m_videoSink, "sink"); +#if GST_CHECK_VERSION(1,0,0) + GstCaps *sinkCaps = gst_pad_query_caps(sinkPad, NULL); +#else GstCaps *sinkCaps = gst_pad_get_caps(sinkPad); +#endif #if (GST_VERSION_MAJOR == 0) && ((GST_VERSION_MINOR < 10) || (GST_VERSION_MICRO < 33)) if (!factory_can_src_any_caps(factory, sinkCaps)) @@ -1652,8 +1712,10 @@ void QGstreamerPlayerSession::handleElementAdded(GstBin *bin, GstElement *elemen // Disable on-disk buffering. g_object_set(G_OBJECT(element), "temp-template", NULL, NULL); } else if (g_str_has_prefix(elementName, "uridecodebin") || - g_str_has_prefix(elementName, "decodebin2")) { - +#if GST_CHECK_VERSION(1,0,0) + g_str_has_prefix(elementName, "decodebin")) { +#else + g_str_has_prefix(elementName, "decodebin2")) { if (g_str_has_prefix(elementName, "uridecodebin")) { // Add video/x-surface (VAAPI) to default raw formats g_object_set(G_OBJECT(element), "caps", gst_static_caps_get(&static_RawCaps), NULL); @@ -1661,7 +1723,7 @@ void QGstreamerPlayerSession::handleElementAdded(GstBin *bin, GstElement *elemen // video sink doesn't support it g_signal_connect(element, "autoplug-select", G_CALLBACK(handleAutoplugSelect), session); } - +#endif //listen for queue2 element added to uridecodebin/decodebin2 as well. //Don't touch other bins since they may have unrelated queues g_signal_connect(element, "element-added", @@ -1711,68 +1773,30 @@ void QGstreamerPlayerSession::showPrerollFrames(bool enabled) void QGstreamerPlayerSession::addProbe(QGstreamerVideoProbeControl* probe) { - QMutexLocker locker(&m_videoProbeMutex); - - if (m_videoProbes.contains(probe)) - return; - - m_videoProbes.append(probe); + Q_ASSERT(!m_videoProbe); + m_videoProbe = probe; + addVideoBufferProbe(); } void QGstreamerPlayerSession::removeProbe(QGstreamerVideoProbeControl* probe) { - QMutexLocker locker(&m_videoProbeMutex); - m_videoProbes.removeOne(probe); - // Do not emit flush signal in this case. - // Assume user releases any outstanding references to video frames. -} - -gboolean QGstreamerPlayerSession::padVideoBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) -{ - Q_UNUSED(pad); - - QGstreamerPlayerSession *session = reinterpret_cast(user_data); - QMutexLocker locker(&session->m_videoProbeMutex); - - if (session->m_videoProbes.isEmpty()) - return TRUE; - - foreach (QGstreamerVideoProbeControl* probe, session->m_videoProbes) - probe->bufferProbed(buffer); - - return TRUE; + Q_ASSERT(m_videoProbe == probe); + removeVideoBufferProbe(); + m_videoProbe = 0; } void QGstreamerPlayerSession::addProbe(QGstreamerAudioProbeControl* probe) { - QMutexLocker locker(&m_audioProbeMutex); - - if (m_audioProbes.contains(probe)) - return; - - m_audioProbes.append(probe); + Q_ASSERT(!m_audioProbe); + m_audioProbe = probe; + addAudioBufferProbe(); } void QGstreamerPlayerSession::removeProbe(QGstreamerAudioProbeControl* probe) { - QMutexLocker locker(&m_audioProbeMutex); - m_audioProbes.removeOne(probe); -} - -gboolean QGstreamerPlayerSession::padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) -{ - Q_UNUSED(pad); - - QGstreamerPlayerSession *session = reinterpret_cast(user_data); - QMutexLocker locker(&session->m_audioProbeMutex); - - if (session->m_audioProbes.isEmpty()) - return TRUE; - - foreach (QGstreamerAudioProbeControl* probe, session->m_audioProbes) - probe->bufferProbed(buffer); - - return TRUE; + Q_ASSERT(m_audioProbe == probe); + removeAudioBufferProbe(); + m_audioProbe = 0; } // This function is similar to stop(), @@ -1797,80 +1821,62 @@ void QGstreamerPlayerSession::endOfMediaReset() void QGstreamerPlayerSession::removeVideoBufferProbe() { - if (m_videoBufferProbeId == -1) + if (!m_videoProbe) return; - if (!m_videoSink) { - m_videoBufferProbeId = -1; - return; - } - GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); if (pad) { - gst_pad_remove_buffer_probe(pad, m_videoBufferProbeId); + m_videoProbe->removeProbeFromPad(pad); gst_object_unref(GST_OBJECT(pad)); } - - m_videoBufferProbeId = -1; } void QGstreamerPlayerSession::addVideoBufferProbe() { - Q_ASSERT(m_videoBufferProbeId == -1); - if (!m_videoSink) + if (!m_videoProbe) return; GstPad *pad = gst_element_get_static_pad(m_videoSink, "sink"); if (pad) { - m_videoBufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padVideoBufferProbe), this); + m_videoProbe->addProbeToPad(pad); gst_object_unref(GST_OBJECT(pad)); } } void QGstreamerPlayerSession::removeAudioBufferProbe() { - if (m_audioBufferProbeId == -1) - return; - - if (!m_audioSink) { - m_audioBufferProbeId = -1; + if (!m_audioProbe) return; - } GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); if (pad) { - gst_pad_remove_buffer_probe(pad, m_audioBufferProbeId); + m_audioProbe->removeProbeFromPad(pad); gst_object_unref(GST_OBJECT(pad)); } - - m_audioBufferProbeId = -1; } void QGstreamerPlayerSession::addAudioBufferProbe() { - Q_ASSERT(m_audioBufferProbeId == -1); - if (!m_audioSink) + if (!m_audioProbe) return; GstPad *pad = gst_element_get_static_pad(m_audioSink, "sink"); if (pad) { - m_audioBufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padAudioBufferProbe), this); + m_audioProbe->addProbeToPad(pad); gst_object_unref(GST_OBJECT(pad)); } } void QGstreamerPlayerSession::flushVideoProbes() { - QMutexLocker locker(&m_videoProbeMutex); - foreach (QGstreamerVideoProbeControl* probe, m_videoProbes) - probe->startFlushing(); + if (m_videoProbe) + m_videoProbe->startFlushing(); } void QGstreamerPlayerSession::resumeVideoProbes() { - QMutexLocker locker(&m_videoProbeMutex); - foreach (QGstreamerVideoProbeControl* probe, m_videoProbes) - probe->stopFlushing(); + if (m_videoProbe) + m_videoProbe->stopFlushing(); } void QGstreamerPlayerSession::playlistTypeFindFunction(GstTypeFind *find, gpointer userData) @@ -1878,7 +1884,11 @@ void QGstreamerPlayerSession::playlistTypeFindFunction(GstTypeFind *find, gpoint QGstreamerPlayerSession* session = (QGstreamerPlayerSession*)userData; const gchar *uri = 0; +#if GST_CHECK_VERSION(1,0,0) + g_object_get(G_OBJECT(session->m_playbin), "current-uri", &uri, NULL); +#else g_object_get(G_OBJECT(session->m_playbin), "uri", &uri, NULL); +#endif guint64 length = gst_type_find_get_length(find); if (!length) @@ -1887,7 +1897,7 @@ void QGstreamerPlayerSession::playlistTypeFindFunction(GstTypeFind *find, gpoint length = qMin(length, guint64(1024)); while (length > 0) { - guint8 *data = gst_type_find_peek(find, 0, length); + const guint8 *data = gst_type_find_peek(find, 0, length); if (data) { session->m_isPlaylist = (QPlaylistFileParser::findPlaylistType(QString::fromUtf8(uri), 0, data, length) != QPlaylistFileParser::UNKNOWN); return; diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h index f2e760ae9..92b4a0cfc 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h @@ -119,11 +119,9 @@ public: void addProbe(QGstreamerVideoProbeControl* probe); void removeProbe(QGstreamerVideoProbeControl* probe); - static gboolean padVideoBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); void addProbe(QGstreamerAudioProbeControl* probe); void removeProbe(QGstreamerAudioProbeControl* probe); - static gboolean padAudioBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); void endOfMediaReset(); @@ -172,7 +170,9 @@ private: static void playbinNotifySource(GObject *o, GParamSpec *p, gpointer d); static void handleVolumeChange(GObject *o, GParamSpec *p, gpointer d); static void handleMutedChange(GObject *o, GParamSpec *p, gpointer d); +#if !GST_CHECK_VERSION(1,0,0) static void insertColorSpaceElement(GstElement *element, gpointer data); +#endif static void handleElementAdded(GstBin *bin, GstElement *element, QGstreamerPlayerSession *session); static void handleStreamsChange(GstBin *bin, gpointer user_data); static GstAutoplugSelectResult handleAutoplugSelect(GstBin *bin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, QGstreamerPlayerSession *session); @@ -194,11 +194,14 @@ private: QGstreamerBusHelper* m_busHelper; GstElement* m_playbin; + GstElement* m_videoSink; + GstElement* m_videoOutputBin; GstElement* m_videoIdentity; +#if !GST_CHECK_VERSION(1,0,0) GstElement* m_colorSpace; bool m_usingColorspaceElement; - GstElement* m_videoSink; +#endif GstElement* m_pendingVideoSink; GstElement* m_nullVideoSink; @@ -218,13 +221,8 @@ private: QList m_streamTypes; QMap m_playbin2StreamOffset; - QList m_videoProbes; - QMutex m_videoProbeMutex; - int m_videoBufferProbeId; - - QList m_audioProbes; - QMutex m_audioProbeMutex; - int m_audioBufferProbeId; + QGstreamerVideoProbeControl *m_videoProbe; + QGstreamerAudioProbeControl *m_audioProbe; int m_volume; qreal m_playbackRate; @@ -252,6 +250,7 @@ private: bool m_isLiveSource; bool m_isPlaylist; + gulong pad_probe_id; }; QT_END_NAMESPACE -- cgit v1.2.3