From 8351c569af51562d8b5e1d0de0b35c42e95b79b5 Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Fri, 6 Sep 2019 12:11:36 +0200 Subject: Fix leaking QVideoFilterRunnable when window is closed When QDeclarativeVideoOutput::releaseResources() is called, means the window is going to be destroyed and it is too late to use scheduleRenderJob() to clear filters. This beforeSynchronizingJobs will not be handled in this case. Which produces a leak of filter's runnable. Fixes: QTBUG-51588 Change-Id: I71ec351463a2c4136d8b7e241d61e628a1b0c64b Reviewed-by: Timur Pocheptsov --- src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp index 40568d118..863cefa4e 100644 --- a/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp @@ -163,8 +163,7 @@ void QDeclarativeVideoRendererBackend::scheduleDeleteFilterResources() void QDeclarativeVideoRendererBackend::releaseResources() { // Called on the gui thread when the window is closed or changed. - QMutexLocker lock(&m_frameMutex); - scheduleDeleteFilterResources(); + invalidateSceneGraph(); } void QDeclarativeVideoRendererBackend::invalidateSceneGraph() -- cgit v1.2.1 From 494eafa612a88ca8edd8dfc128408ea651fa2a1e Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Wed, 11 Sep 2019 15:39:00 +0200 Subject: DirectShow: Fix tst_QMediaPlayerBackend::seekPauseSeek Fixed tests about start times. Since DirectShow can provide frames with negative start time, also from the beginning, also pixels can vary, e.g. (rgb) 255:0:0 would be 230:19:19 after decoding. Task-number: QTBUG-46368 Change-Id: I025f7cf9238ddf9ba2a04f63e3e54b77f3cceafe Reviewed-by: Ville Voutilainen --- .../qmediaplayerbackend/tst_qmediaplayerbackend.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp index 9d29025c8..df218be7f 100644 --- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp +++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp @@ -791,16 +791,18 @@ void tst_QMediaPlayerBackend::seekPauseSeek() player.pause(); QTRY_COMPARE(player.state(), QMediaPlayer::PausedState); // it might take some time for the operation to be completed - QTRY_VERIFY(!surface->m_frameList.isEmpty()); // we must see a frame at position 7000 here + QTRY_VERIFY_WITH_TIMEOUT(!surface->m_frameList.isEmpty(), 10000); // we must see a frame at position 7000 here // Make sure that the frame has a timestamp before testing - not all backends provides this - if (surface->m_frameList.back().startTime() < 0) + if (!surface->m_frameList.back().isValid() || surface->m_frameList.back().startTime() < 0) QSKIP("No timestamp"); { QVideoFrame frame = surface->m_frameList.back(); +#if !QT_CONFIG(directshow) const qint64 elapsed = (frame.startTime() / 1000) - position; // frame.startTime() is microsecond, position is milliseconds. QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData()); +#endif QCOMPARE(frame.width(), 160); QCOMPARE(frame.height(), 120); @@ -808,9 +810,9 @@ void tst_QMediaPlayerBackend::seekPauseSeek() QVERIFY(frame.map(QAbstractVideoBuffer::ReadOnly)); QImage image(frame.bits(), frame.width(), frame.height(), QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat())); QVERIFY(!image.isNull()); - QVERIFY(qRed(image.pixel(0, 0)) >= 240); // conversion from YUV => RGB, that's why it's not 255 - QCOMPARE(qGreen(image.pixel(0, 0)), 0); - QCOMPARE(qBlue(image.pixel(0, 0)), 0); + QVERIFY(qRed(image.pixel(0, 0)) >= 230); // conversion from YUV => RGB, that's why it's not 255 + QVERIFY(qGreen(image.pixel(0, 0)) < 20); + QVERIFY(qBlue(image.pixel(0, 0)) < 20); frame.unmap(); } @@ -824,17 +826,19 @@ void tst_QMediaPlayerBackend::seekPauseSeek() { QVideoFrame frame = surface->m_frameList.back(); +#if !QT_CONFIG(directshow) const qint64 elapsed = (frame.startTime() / 1000) - position; QVERIFY2(qAbs(elapsed) < (qint64)500, QByteArray::number(elapsed).constData()); +#endif QCOMPARE(frame.width(), 160); QCOMPARE(frame.height(), 120); QVERIFY(frame.map(QAbstractVideoBuffer::ReadOnly)); QImage image(frame.bits(), frame.width(), frame.height(), QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat())); QVERIFY(!image.isNull()); - QCOMPARE(qRed(image.pixel(0, 0)), 0); - QVERIFY(qGreen(image.pixel(0, 0)) >= 240); - QCOMPARE(qBlue(image.pixel(0, 0)), 0); + QVERIFY(qRed(image.pixel(0, 0)) < 20); + QVERIFY(qGreen(image.pixel(0, 0)) >= 230); + QVERIFY(qBlue(image.pixel(0, 0)) < 20); frame.unmap(); } } -- cgit v1.2.1 From 163846dba1ef9171699b5203cf9bfbb91a609df0 Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Fri, 13 Sep 2019 13:18:57 +0200 Subject: DirectShow: Emit media status before error Media status should be emitted before any error. Since error can cause changing the media and changing to new media status. Fixes BFAIL : tst_QMediaPlayerBackend::playlistObject() Compared values are not the same Actual (mediaStatusSpy.count()): 4 Expected (6) : 6 Task-number: QTBUG-46368 Change-Id: I8eb82e7a551d78abd143baa594cfda9bacd7e544 Reviewed-by: Ville Voutilainen --- src/plugins/directshow/player/directshowplayercontrol.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp index 7de0a686f..14f272f17 100644 --- a/src/plugins/directshow/player/directshowplayercontrol.cpp +++ b/src/plugins/directshow/player/directshowplayercontrol.cpp @@ -293,6 +293,9 @@ void DirectShowPlayerControl::emitPropertyChanges() int properties = m_updateProperties; m_updateProperties = 0; + if (properties & StatusProperty) + emit mediaStatusChanged(m_status); + if ((properties & ErrorProperty) && m_error != QMediaPlayer::NoError) emit error(m_error, m_errorString); @@ -313,9 +316,6 @@ void DirectShowPlayerControl::emitPropertyChanges() if (properties & SeekableProperty) emit seekableChanged(m_seekable); - if (properties & StatusProperty) - emit mediaStatusChanged(m_status); - if (properties & StateProperty) emit stateChanged(m_state); } -- cgit v1.2.1 From 9eef3390241f8c6e25c5ccc7b1baa25058146927 Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Tue, 10 Sep 2019 11:37:39 +0200 Subject: GStreamer: Recreate playbin after custom pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if custom pipeline is set, old playbin elements are destroyed. And if normal url is passed again after the custom pipeline, it is unable to play. Because no playbin elements exist anymore. Adding a fix to recreate playbin elements if "not a pipeline url" is passed. Fixes: QTBUG-78079 Change-Id: I8b3498c4660639f5d757b322a136846ee82fdc28 Reviewed-by: Christian Strømme --- src/gsttools/qgstreamerplayersession.cpp | 90 ++++++++++++++++++++++---------- src/gsttools/qgstreamerplayersession_p.h | 3 ++ 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index 1a74de574..c0998d7ae 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -112,6 +112,11 @@ static GstStaticCaps static_RawCaps = GST_STATIC_CAPS(DEFAULT_RAW_CAPS); QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) : QObject(parent) +{ + initPlaybin(); +} + +void QGstreamerPlayerSession::initPlaybin() { m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, NULL); if (m_playbin) { @@ -186,9 +191,7 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) if (m_playbin != 0) { // Sort out messages - m_bus = gst_element_get_bus(m_playbin); - m_busHelper = new QGstreamerBusHelper(m_bus, this); - m_busHelper->installMessageFilter(this); + setBus(gst_element_get_bus(m_playbin)); g_object_set(G_OBJECT(m_playbin), "video-sink", m_videoOutputBin, NULL); @@ -224,16 +227,33 @@ QGstreamerPlayerSession::~QGstreamerPlayerSession() removeAudioBufferProbe(); delete m_busHelper; - gst_object_unref(GST_OBJECT(m_bus)); - if (m_playbin) - gst_object_unref(GST_OBJECT(m_playbin)); - gst_object_unref(GST_OBJECT(m_pipeline)); + m_busHelper = nullptr; + resetElements(); + } +} + +template +static inline void resetGstObject(T *&obj, T *v = nullptr) +{ + if (obj) + gst_object_unref(GST_OBJECT(obj)); + + obj = v; +} + +void QGstreamerPlayerSession::resetElements() +{ + setBus(nullptr); + resetGstObject(m_playbin); + resetGstObject(m_pipeline); #if !GST_CHECK_VERSION(1,0,0) - gst_object_unref(GST_OBJECT(m_colorSpace)); + resetGstObject(m_colorSpace); #endif - gst_object_unref(GST_OBJECT(m_nullVideoSink)); - gst_object_unref(GST_OBJECT(m_videoOutputBin)); - } + resetGstObject(m_nullVideoSink); + resetGstObject(m_videoOutputBin); + + m_volumeElement = nullptr; + m_videoIdentity = nullptr; } GstElement *QGstreamerPlayerSession::playbin() const @@ -323,8 +343,14 @@ void QGstreamerPlayerSession::loadFromUri(const QNetworkRequest &request) bool QGstreamerPlayerSession::parsePipeline() { - if (m_request.url().scheme() != QLatin1String("gst-pipeline")) + if (m_request.url().scheme() != QLatin1String("gst-pipeline")) { + if (!m_playbin) { + resetElements(); + initPlaybin(); + updateVideoRenderer(); + } return false; + } // Set current surface to video sink before creating a pipeline. auto renderer = qobject_cast(m_videoOutput); @@ -371,25 +397,12 @@ bool QGstreamerPlayerSession::setPipeline(GstElement *pipeline) if (!bus) return false; - gst_object_unref(GST_OBJECT(m_pipeline)); - m_pipeline = pipeline; - gst_object_unref(GST_OBJECT(m_bus)); - m_bus = bus; - m_busHelper->deleteLater(); - m_busHelper = new QGstreamerBusHelper(m_bus, this); - m_busHelper->installMessageFilter(this); - - if (m_videoOutput) - m_busHelper->installMessageFilter(m_videoOutput); - - if (m_playbin) { + if (m_playbin) gst_element_set_state(m_playbin, GST_STATE_NULL); - gst_object_unref(GST_OBJECT(m_playbin)); - } - m_playbin = nullptr; - m_volumeElement = nullptr; - m_videoIdentity = nullptr; + resetElements(); + setBus(bus); + m_pipeline = pipeline; if (m_renderer) { gst_foreach(gst_bin_iterate_sinks(GST_BIN(pipeline)), @@ -419,6 +432,25 @@ bool QGstreamerPlayerSession::setPipeline(GstElement *pipeline) return true; } +void QGstreamerPlayerSession::setBus(GstBus *bus) +{ + resetGstObject(m_bus, bus); + + // It might still accept gst messages. + if (m_busHelper) + m_busHelper->deleteLater(); + m_busHelper = nullptr; + + if (!m_bus) + return; + + m_busHelper = new QGstreamerBusHelper(m_bus, this); + m_busHelper->installMessageFilter(this); + + if (m_videoOutput) + m_busHelper->installMessageFilter(m_videoOutput); +} + qint64 QGstreamerPlayerSession::duration() const { return m_duration; diff --git a/src/gsttools/qgstreamerplayersession_p.h b/src/gsttools/qgstreamerplayersession_p.h index 69027eeb2..797229e69 100644 --- a/src/gsttools/qgstreamerplayersession_p.h +++ b/src/gsttools/qgstreamerplayersession_p.h @@ -210,6 +210,9 @@ private: void resumeVideoProbes(); bool parsePipeline(); bool setPipeline(GstElement *pipeline); + void resetElements(); + void initPlaybin(); + void setBus(GstBus *bus); QNetworkRequest m_request; QMediaPlayer::State m_state = QMediaPlayer::StoppedState; -- cgit v1.2.1 From 7aa08ffafd5ac69bca02038aae80ea014f444174 Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Fri, 6 Sep 2019 16:14:46 +0200 Subject: DirectShow: Don't seek if playback is stopped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the playback is already stopped, and after that new position is requested twice, e.g. stop(); setPosition(0); setPosition(0); this causes the playback to start. Need to postpone setting of new position until play is requested. Also no need to emit positionChanged when position is not changed. Fixes: QTBUG-68778 Change-Id: Ib62942ee155d9c8ed2310132dcc6b501dfef1a04 Reviewed-by: Christian Strømme --- src/plugins/directshow/player/directshowplayercontrol.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp index 14f272f17..87bd53bc4 100644 --- a/src/plugins/directshow/player/directshowplayercontrol.cpp +++ b/src/plugins/directshow/player/directshowplayercontrol.cpp @@ -102,9 +102,11 @@ void DirectShowPlayerControl::setPosition(qint64 position) emit mediaStatusChanged(m_status); } - if (m_state == QMediaPlayer::StoppedState && m_pendingPosition != position) { - m_pendingPosition = position; - emit positionChanged(m_pendingPosition); + if (m_state == QMediaPlayer::StoppedState) { + if (m_pendingPosition != position) { + m_pendingPosition = position; + emit positionChanged(m_pendingPosition); + } return; } -- cgit v1.2.1 From 182b4b56011b70fa9afdedf53d3c25f73028c335 Mon Sep 17 00:00:00 2001 From: Ratchanan Srirattanamet Date: Fri, 27 Sep 2019 23:31:57 +0700 Subject: Gstreamer: also remove features when simplifying caps Some element can return caps with same resolution/framerate but different features. gst_simplify_caps() won't de-duplicate this, resulting in duplicated entries for the same resolution/framerate. This commit add calls to also remove features from the caps in addition to setting name to video/x-raw. The GST_CHECK_VERSION macro is added to maintain compatibility with earlier version of Gstreamer without GstCapsFeatures. [ChangeLog][CameraBin] Fixed duplicated entries in supported resolution/ framerate if the underlying element uses caps' features. Change-Id: I15101899eb0369925013ccc1d925afb890a01205 Reviewed-by: VaL Doroshchuk --- src/plugins/gstreamer/camerabin/camerabinsession.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index b96ba6792..3e505a413 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -1312,6 +1312,9 @@ QList< QPair > CameraBinSession::supportedFrameRates(const QSize &frame for (uint i=0; i CameraBinSession::supportedResolutions(QPair rate, for (uint i=0; i Date: Tue, 17 Sep 2019 16:00:55 +0200 Subject: Override QGraphicsItem::type in QGraphicsVideoItem Fixes: QTBUG-45064 Change-Id: I6b4a7a4b92affe80fc288e08d514092ac3d6899a Reviewed-by: Ville Voutilainen --- src/multimediawidgets/qgraphicsvideoitem.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/multimediawidgets/qgraphicsvideoitem.h b/src/multimediawidgets/qgraphicsvideoitem.h index 3902389d9..5aa3bd75c 100644 --- a/src/multimediawidgets/qgraphicsvideoitem.h +++ b/src/multimediawidgets/qgraphicsvideoitem.h @@ -81,6 +81,15 @@ public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + enum { Type = 14 }; + int type() const override + { + // Enable the use of qgraphicsitem_cast with this item. + return Type; + } +#endif + Q_SIGNALS: void nativeSizeChanged(const QSizeF &size); -- cgit v1.2.1 From c7bd3131a8854814d59881339ac1d7a4af3c9863 Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Wed, 11 Sep 2019 11:09:52 +0200 Subject: imx6: Fix mapping VPU texture using real physical address of data * Get real physical address of video frame data and provide it to imx6 vivante videonode to prevent memory leak from kernel. By default qtmultimedia does not depend on gstreamer-imx, but the plugin should exist on imx platform. So gstreamer_imxcommon will be disabled. And should be manually enabled in yocto receipt. Change-Id: I6e75746a2ba651d12bdd7697ae483699aad450fc Fixes: QTBUG-73084 Reviewed-by: Ville Voutilainen --- src/multimedia/configure.json | 17 +++++++++++++++++ src/plugins/videonode/imx6/imx6.pro | 6 ++++++ src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp | 12 +++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/multimedia/configure.json b/src/multimedia/configure.json index 6d56af5ed..2db8ce55c 100644 --- a/src/multimedia/configure.json +++ b/src/multimedia/configure.json @@ -95,6 +95,18 @@ { "libs": "-lgstphotography-1.0" } ] }, + "gstreamer_imxcommon": { + "label": "GStreamer i.MX common", + "export": "gstreamer_imxcommon", + "test": { + "include": "gst/allocators/imx/phys_mem_meta.h" + }, + "use": "gstreamer_1_0", + "sources": [ + { "type": "pkgConfig", + "args": "gstimxcommon" } + ] + }, "libresourceqt5": { "label": "libresourceqt5", "test": "resourcepolicy", @@ -229,6 +241,11 @@ "condition": "(features.gstreamer_1_0 && libs.gstreamer_photography_1_0) || (features.gstreamer_0_10 && libs.gstreamer_photography_0_10)", "output": [ "privateFeature" ] }, + "gstreamer_imxcommon": { + "label": "GStreamer i.MX common", + "condition": "(features.gstreamer_1_0 && libs.gstreamer_imxcommon)", + "output": [ "privateFeature" ] + }, "gpu_vivante": { "label": "Vivante GPU", "condition": "features.gui && features.opengles2 && tests.gpu_vivante", diff --git a/src/plugins/videonode/imx6/imx6.pro b/src/plugins/videonode/imx6/imx6.pro index c8085a31e..43e17e725 100644 --- a/src/plugins/videonode/imx6/imx6.pro +++ b/src/plugins/videonode/imx6/imx6.pro @@ -2,6 +2,12 @@ TARGET = imx6vivantevideonode QT += multimedia-private qtmultimediaquicktools-private +qtConfig(gstreamer_imxcommon) { + QT += multimediagsttools-private + QMAKE_USE += gstreamer_imxcommon + DEFINES += GST_USE_UNSTABLE_API +} + HEADERS += \ qsgvivantevideonode.h \ qsgvivantevideomaterialshader.h \ diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp index 4b68f47a4..e1468fe34 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp +++ b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp @@ -49,6 +49,11 @@ #include +#include +#if QT_CONFIG(gstreamer_imxcommon) +#include "private/qgstvideobuffer_p.h" +#include +#endif //#define QT_VIVANTE_VIDEO_DEBUG @@ -224,7 +229,12 @@ GLuint QSGVivanteVideoMaterial::vivanteMapping(QVideoFrame vF) #endif GLuint physical = ~0U; - +#if QT_CONFIG(gstreamer_imxcommon) + auto buffer = reinterpret_cast(vF.buffer()); + GstImxPhysMemMeta *meta = GST_IMX_PHYS_MEM_META_GET(buffer->buffer()); + if (meta && meta->phys_addr) + physical = meta->phys_addr; +#endif glBindTexture(GL_TEXTURE_2D, tmpTexId); glTexDirectVIVMap_LOCAL(GL_TEXTURE_2D, fullWidth, fullHeight, -- cgit v1.2.1 From d2475bdd4cbb9a70abf19db465ddbdbf29591568 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Mon, 7 Oct 2019 08:23:11 +0200 Subject: Fix build with ALSA in non-system location This amends commit f0ea2641. Fixes: QTBUG-79014 Change-Id: Ib04d844ef64e335785408dc8d7e512a5a3651655 Reviewed-by: VaL Doroshchuk --- src/plugins/alsa/alsa.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/alsa/alsa.pro b/src/plugins/alsa/alsa.pro index 56657f100..4012bb8f6 100644 --- a/src/plugins/alsa/alsa.pro +++ b/src/plugins/alsa/alsa.pro @@ -1,7 +1,7 @@ TARGET = qtaudio_alsa QT += multimedia-private -LIBS += -lasound +QMAKE_USE += alsa HEADERS += \ qalsaplugin.h \ -- cgit v1.2.1