From 1cc12b8cdda2f9a0477be987824540af84deeb53 Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Fri, 12 Apr 2019 09:37:38 +0200 Subject: DirectShow: Don't set the same media MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes tst_QMediaPlayerBackend::playlistObject() Actual (currentMediaSpy.count()): 2 Expected (1) : 1 .\tst_qmediaplayerbackend.cpp(1224) : failure location Task-number: QTBUG-65574 Change-Id: Ia7cbcb4a22ee43df9e1efff065910b084bdbf00e Reviewed-by: Oliver Wolff Reviewed-by: Christian Strømme --- src/plugins/directshow/player/directshowplayercontrol.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp index 2d0ee2d59..7de0a686f 100644 --- a/src/plugins/directshow/player/directshowplayercontrol.cpp +++ b/src/plugins/directshow/player/directshowplayercontrol.cpp @@ -219,6 +219,9 @@ const QIODevice *DirectShowPlayerControl::mediaStream() const void DirectShowPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream) { + if (m_media == media) + return; + m_pendingPosition = -1; m_emitPosition = -1; -- cgit v1.2.3 From 14d68efadc43265c640dcd6796d947890d14f15e Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Wed, 10 Apr 2019 16:03:17 +0200 Subject: DirectShow: Don't update status on pending tasks when graph is loaded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests expect to see updated playback position together with QMediaPlayer::BufferedMedia. But currently QMediaPlayer::BufferedMedia is emitted before updating the position. Removed updating the status if the graph is already loaded but a task is still pending. Fixes tst_QMediaPlayerBackend::seekInStoppedState Task-number: QTBUG-65574 Change-Id: I66d214312dbf31973a13b5154a52599aa517f38c Reviewed-by: Oliver Wolff Reviewed-by: Christian Strømme --- src/plugins/directshow/player/directshowplayerservice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index 3974c59a5..bf7cbbfe4 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -1617,7 +1617,7 @@ void DirectShowPlayerService::updateStatus() m_playerControl->updateStatus(QMediaPlayer::LoadingMedia); break; case Loaded: - if ((m_pendingTasks | m_executingTask | m_executedTasks) & (Play | Pause)) { + if ((m_executingTask | m_executedTasks) & (Play | Pause)) { if (m_buffering) m_playerControl->updateStatus(QMediaPlayer::BufferingMedia); else -- cgit v1.2.3 From c4de056a6aa44567cdbf2ce91a464e597ad4af8f Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Wed, 10 Apr 2019 15:50:48 +0200 Subject: EVR: Remove setting of start/end times in QVideoFrame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to unknown bug in IMFTransform->ProcessOutput() when after seeking sample times are not set correctly to provided IMFSample. This causes current playback position to be not respected in video samples. Even if you seeked to higher/lower position, sample time is always counted from beginning. Which makes no sense to use this feature because video frame's sample times are not related to current position. To test it, need just seek to arbitrary position and check how startTime is not correlated to new position. Fixes tst_QMediaPlayerBackend::seekPauseSeek Task-number: QTBUG-65574 Change-Id: I897d75c055347cdcca38a84dc18f91800d070c09 Reviewed-by: Christian Strømme --- src/plugins/common/evr/evrd3dpresentengine.cpp | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'src') diff --git a/src/plugins/common/evr/evrd3dpresentengine.cpp b/src/plugins/common/evr/evrd3dpresentengine.cpp index ab694b795..4ce5a99d7 100644 --- a/src/plugins/common/evr/evrd3dpresentengine.cpp +++ b/src/plugins/common/evr/evrd3dpresentengine.cpp @@ -593,16 +593,6 @@ QVideoFrame D3DPresentEngine::makeVideoFrame(IMFSample *sample) m_surfaceFormat.frameSize(), m_surfaceFormat.pixelFormat()); - // WMF uses 100-nanosecond units, Qt uses microseconds - LONGLONG startTime = -1; - if (SUCCEEDED(sample->GetSampleTime(&startTime))) { - frame.setStartTime(startTime * 0.1); - - LONGLONG duration = -1; - if (SUCCEEDED(sample->GetSampleDuration(&duration))) - frame.setEndTime((startTime + duration) * 0.1); - } - return frame; } -- cgit v1.2.3 From 72101558b3afc40ef41132e66ce789b92929e911 Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Thu, 25 Apr 2019 12:24:15 +0200 Subject: Gstreamer: Fix deadlock when state is requested in ASYNC mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When new media is set to the player, updating of "show-preroll-frame" property from the video sink is also requested: g_object_set(G_OBJECT(m_videoSink), "show-preroll-frame", value, NULL); This produces emitting of a signal: g_signal_connect(G_OBJECT(sink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), sink); Inside handleShowPrerollChange() the state of the video sink is requested with infinite timeout: gst_element_get_state(GST_ELEMENT(sink), &state, NULL, GST_CLOCK_TIME_NONE); In case if the video sink performed an ASYNC state change, means changing of the state is pending and need to wait, this function will block up for infinite timeout. But probably changing of the state is requested (and should be performed) on the same thread where it is waiting for this change, it produces a deadlock. Changing timeout to 10ms to avoid this block. Fixes: QTBUG-72468 Change-Id: I06235ccfb8f76423f65ed192d5e8de6e60723e72 Reviewed-by: Christian Strømme --- src/gsttools/qgstvideorenderersink.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gsttools/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp index 09fdd42a6..c3a7a5988 100644 --- a/src/gsttools/qgstvideorenderersink.cpp +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -516,7 +516,8 @@ void QGstVideoRendererSink::handleShowPrerollChange(GObject *o, GParamSpec *p, g if (!showPrerollFrame) { GstState state = GST_STATE_VOID_PENDING; - gst_element_get_state(GST_ELEMENT(sink), &state, NULL, GST_CLOCK_TIME_NONE); + GstClockTime timeout = 10000000; // 10 ms + gst_element_get_state(GST_ELEMENT(sink), &state, NULL, timeout); // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means // the QMediaPlayer was stopped from the paused state. // We need to flush the current frame. -- cgit v1.2.3 From 19232dbe27521f60bdd265dbac0419d93c7ea5de Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Tue, 12 Mar 2019 10:28:46 +0100 Subject: Use QOpenGLContext::makeCurrent if QGLContext::makeCurrent failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If QPainterVideoSurface::setGLContext(QGLContext::currentContext()) is used and no valid paint device has been provided to QGLContext, it fails to make the context current. Thus there might be no available current context which might also produce crashes. QGLContext::currentContext() creates QGLContext based on current QOpenGLContext instance, and no paint device is available in QGLContext::device() in this case. Currently QVideoWidget and QGraphicsVideoItem are effected. It is relevant when a renderer calls currentContext->doneCurrent() before using the QPainterVideoSurface. After this there is no any current context which could cause a crash, e.g. within compiling the shaders. Task-number: QTBUG-74277 Change-Id: Ia28f4f6843a82a897399fd1ce2463e3b087b6437 Reviewed-by: Christian Strømme --- src/multimediawidgets/qpaintervideosurface.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/multimediawidgets/qpaintervideosurface.cpp b/src/multimediawidgets/qpaintervideosurface.cpp index e4762a7e1..440d5c858 100644 --- a/src/multimediawidgets/qpaintervideosurface.cpp +++ b/src/multimediawidgets/qpaintervideosurface.cpp @@ -62,6 +62,15 @@ #include QT_BEGIN_NAMESPACE +static void makeCurrent(QGLContext *context) +{ + context->makeCurrent(); + + auto handle = context->contextHandle(); + if (handle && QOpenGLContext::currentContext() != handle) + handle->makeCurrent(handle->surface()); +} + QVideoSurfacePainter::~QVideoSurfacePainter() { } @@ -395,7 +404,7 @@ QAbstractVideoSurface::Error QVideoSurfaceGLPainter::setCurrentFrame(const QVide glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) { - m_context->makeCurrent(); + makeCurrent(m_context); for (int i = 0; i < m_textureCount; ++i) { glBindTexture(GL_TEXTURE_2D, m_textureIds[i]); @@ -737,7 +746,7 @@ QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::start(const QVideoSurfac QAbstractVideoSurface::Error error = QAbstractVideoSurface::NoError; - m_context->makeCurrent(); + makeCurrent(m_context); const char *program = 0; @@ -862,7 +871,7 @@ QAbstractVideoSurface::Error QVideoSurfaceArbFpPainter::start(const QVideoSurfac void QVideoSurfaceArbFpPainter::stop() { if (m_context) { - m_context->makeCurrent(); + makeCurrent(m_context); if (m_handleType != QAbstractVideoBuffer::GLTextureHandle) glDeleteTextures(m_textureCount, m_textureIds); @@ -1115,7 +1124,7 @@ QAbstractVideoSurface::Error QVideoSurfaceGlslPainter::start(const QVideoSurface QAbstractVideoSurface::Error error = QAbstractVideoSurface::NoError; - m_context->makeCurrent(); + makeCurrent(m_context); const char *fragmentProgram = 0; @@ -1222,7 +1231,7 @@ QAbstractVideoSurface::Error QVideoSurfaceGlslPainter::start(const QVideoSurface void QVideoSurfaceGlslPainter::stop() { if (m_context) { - m_context->makeCurrent(); + makeCurrent(m_context); if (m_handleType != QAbstractVideoBuffer::GLTextureHandle) glDeleteTextures(m_textureCount, m_textureIds); @@ -1623,7 +1632,7 @@ void QPainterVideoSurface::setGLContext(QGLContext *context) //Set a dynamic property to access the OpenGL context this->setProperty("GLContext", QVariant::fromValue(m_glContext->contextHandle())); - m_glContext->makeCurrent(); + makeCurrent(m_glContext); const QByteArray extensions(reinterpret_cast( context->contextHandle()->functions()->glGetString(GL_EXTENSIONS))); @@ -1734,13 +1743,13 @@ void QPainterVideoSurface::createPainter() #if !defined(QT_OPENGL_ES) && !defined(QT_OPENGL_DYNAMIC) case FragmentProgramShader: Q_ASSERT(m_glContext); - m_glContext->makeCurrent(); + makeCurrent(m_glContext); m_painter = new QVideoSurfaceArbFpPainter(m_glContext); break; #endif // !QT_OPENGL_ES && !QT_OPENGL_DYNAMIC case GlslShader: Q_ASSERT(m_glContext); - m_glContext->makeCurrent(); + makeCurrent(m_glContext); m_painter = new QVideoSurfaceGlslPainter(m_glContext); break; default: -- cgit v1.2.3 From 7aeebc07b0566b2cf18f3f1f5656eac4d6386aaf Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Mon, 15 Apr 2019 15:21:08 +0200 Subject: Gstreamer: Allow streams in custom pipelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced pushing buffers from QIODevice to appsrc gstreamer element: player->setMedia("gst-pipeline: appsrc ! ...", io_device); Also ported to gst 0.10. Change-Id: I1a84d22c0d5c56fe433d494413c5ab23da7c6bf3 Reviewed-by: Christian Strømme --- src/gsttools/qgstreamerplayersession.cpp | 110 +++++++++++++-------- .../gsttools_headers/qgstreamerplayersession_p.h | 3 +- 2 files changed, 72 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index 1e099fc89..bd28afb91 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -306,7 +306,7 @@ void QGstreamerPlayerSession::loadFromStream(const QNetworkRequest &request, QIO m_appSrc = new QGstAppSrc(this); m_appSrc->setStream(appSrcStream); - if (m_playbin) { + if (!parsePipeline() && m_playbin) { m_tags.clear(); emit tagsChanged(); @@ -338,28 +338,7 @@ void QGstreamerPlayerSession::loadFromUri(const QNetworkRequest &request) } #endif - if (m_request.url().scheme() == QLatin1String("gst-pipeline")) { - // Set current surface to video sink before creating a pipeline. - auto renderer = qobject_cast(m_videoOutput); - if (renderer) - QVideoSurfaceGstSink::setSurface(renderer->surface()); - - QString url = m_request.url().toString(QUrl::RemoveScheme); - QString pipeline = QUrl::fromPercentEncoding(url.toLatin1().constData()); - GError *err = nullptr; - GstElement *element = gst_parse_launch(pipeline.toLatin1().constData(), &err); - if (err) { - auto errstr = QLatin1String(err->message); - qWarning() << "Error:" << pipeline << ":" << errstr; - emit error(QMediaPlayer::FormatError, errstr); - g_clear_error(&err); - } - - setPipeline(element); - return; - } - - if (m_playbin) { + if (!parsePipeline() && m_playbin) { m_tags.clear(); emit tagsChanged(); @@ -374,11 +353,55 @@ void QGstreamerPlayerSession::loadFromUri(const QNetworkRequest &request) } } -void QGstreamerPlayerSession::setPipeline(GstElement *pipeline) +bool QGstreamerPlayerSession::parsePipeline() +{ + if (m_request.url().scheme() != QLatin1String("gst-pipeline")) + return false; + + // Set current surface to video sink before creating a pipeline. + auto renderer = qobject_cast(m_videoOutput); + if (renderer) + QVideoSurfaceGstSink::setSurface(renderer->surface()); + + QString url = m_request.url().toString(QUrl::RemoveScheme); + QString desc = QUrl::fromPercentEncoding(url.toLatin1().constData()); + GError *err = nullptr; + GstElement *pipeline = gst_parse_launch(desc.toLatin1().constData(), &err); + if (err) { + auto errstr = QLatin1String(err->message); + qWarning() << "Error:" << desc << ":" << errstr; + emit error(QMediaPlayer::FormatError, errstr); + g_clear_error(&err); + } + + return setPipeline(pipeline); +} + +static void gst_foreach(GstIterator *it, const std::function &cmp) +{ +#if GST_CHECK_VERSION(1,0,0) + GValue value = G_VALUE_INIT; + while (gst_iterator_next (it, &value) == GST_ITERATOR_OK) { + auto child = static_cast(g_value_get_object(&value)); +#else + GstElement *child = nullptr; + while (gst_iterator_next(it, reinterpret_cast(&child)) == GST_ITERATOR_OK) { +#endif + if (cmp(child)) + break; + } + + gst_iterator_free(it); +#if GST_CHECK_VERSION(1,0,0) + g_value_unset(&value); +#endif +} + +bool QGstreamerPlayerSession::setPipeline(GstElement *pipeline) { GstBus *bus = pipeline ? gst_element_get_bus(pipeline) : nullptr; if (!bus) - return; + return false; gst_object_unref(GST_OBJECT(m_pipeline)); m_pipeline = pipeline; @@ -401,24 +424,31 @@ void QGstreamerPlayerSession::setPipeline(GstElement *pipeline) m_videoIdentity = nullptr; if (m_renderer) { - auto it = gst_bin_iterate_sinks(GST_BIN(pipeline)); -#if GST_CHECK_VERSION(1,0,0) - GValue data = { 0, 0 }; - while (gst_iterator_next (it, &data) == GST_ITERATOR_OK) { - auto child = static_cast(g_value_get_object(&data)); -#else - GstElement *child = nullptr; - while (gst_iterator_next(it, reinterpret_cast(&child)) == GST_ITERATOR_OK) { -#endif - if (QLatin1String(GST_OBJECT_NAME(child)) == QLatin1String("qtvideosink")) { - m_renderer->setVideoSink(child); - break; - } - } - gst_iterator_free(it); + gst_foreach(gst_bin_iterate_sinks(GST_BIN(pipeline)), + [this](GstElement *child) { + if (qstrcmp(GST_OBJECT_NAME(child), "qtvideosink") == 0) { + m_renderer->setVideoSink(child); + return true; + } + return false; + }); } +#if QT_CONFIG(gstreamer_app) + if (m_appSrc) { + gst_foreach(gst_bin_iterate_sources(GST_BIN(pipeline)), + [this](GstElement *child) { + if (qstrcmp(qt_gst_element_get_factory_name(child), "appsrc") == 0) { + m_appSrc->setup(child); + return true; + } + return false; + }); + } +#endif + emit pipelineChanged(); + return true; } qint64 QGstreamerPlayerSession::duration() const diff --git a/src/multimedia/gsttools_headers/qgstreamerplayersession_p.h b/src/multimedia/gsttools_headers/qgstreamerplayersession_p.h index b613793c4..d4b050272 100644 --- a/src/multimedia/gsttools_headers/qgstreamerplayersession_p.h +++ b/src/multimedia/gsttools_headers/qgstreamerplayersession_p.h @@ -208,7 +208,8 @@ private: void addAudioBufferProbe(); void flushVideoProbes(); void resumeVideoProbes(); - void setPipeline(GstElement *pipeline); + bool parsePipeline(); + bool setPipeline(GstElement *pipeline); QNetworkRequest m_request; QMediaPlayer::State m_state; -- cgit v1.2.3 From 1835a9f398c1400a254b5df8f8da2f1e357a45a0 Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Fri, 12 Apr 2019 09:28:12 +0200 Subject: DirectShow: Report about an error for wrong urls If wrong url is passed, no need to try it again. But report an error instead. Also there cannot be empty url, because if an url is empty, NoMedia should be emitted. Fixes tst_QMediaPlayerBackend::playlistObject() Task-number: QTBUG-65574 Change-Id: I658b16cfe7b96c202715651d6b20d01a9af6c746 Reviewed-by: Andy Shaw --- src/plugins/directshow/player/directshowplayerservice.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index bf7cbbfe4..2eeb06159 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -403,8 +403,6 @@ void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker) m_pendingTasks |= SetRate; m_source = source; - } else if (!m_resources.isEmpty()) { - m_pendingTasks |= SetUrlSource; } else { m_graphStatus = InvalidMedia; -- cgit v1.2.3 From 2508aa870167e86fa7e71e982388d5265f11fad1 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 21 May 2019 06:42:46 +0200 Subject: Only call CoUnitialize() if calling CoInitialize() was necessary As CoInitialize() may have been called by something else beforehand then it should only call CoUnitialize() if it returns S_OK which indicates it was not already called. That ensures it is only uninitalized if it did the initialization. Change-Id: I1b723e0b69fe5f9ff7c2c6ef083e9196f21812b2 Reviewed-by: VaL Doroshchuk --- src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp index c054c0f76..70e7fbce5 100644 --- a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp +++ b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp @@ -396,7 +396,7 @@ QList QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode) QList devices; //enumerate device fullnames through directshow api - CoInitialize(NULL); + auto hrCoInit = CoInitialize(nullptr); ICreateDevEnum *pDevEnum = NULL; IEnumMoniker *pEnum = NULL; // Create the System device enumerator @@ -447,7 +447,8 @@ QList QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode) } pDevEnum->Release(); } - CoUninitialize(); + if (SUCCEEDED(hrCoInit)) + CoUninitialize(); return devices; } -- cgit v1.2.3 From 7a4478a4a411df9f21e1499d2301049f9045b7d6 Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Mon, 20 May 2019 12:49:28 +0200 Subject: GStreamer: Fix crash when the bus helper is destroyed Called deleteLater() on the bus helper to ensure the cleanup is handled gracefully. QGstreamerBusHelperPrivate::doProcessMessage is called on a message from gst, which it calls QGstreamerPlayerSession::processBusMessage where new media is set and current helper object is destroyed. When for loop is ended in doProcessMessage, current object is freed and produces a crash. Change-Id: Ic9cde97c284406450d4723f1f7f71fa1ea2c0648 Reviewed-by: Andy Shaw --- src/gsttools/qgstreamerplayersession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index bd28afb91..5c9eebca2 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -407,7 +407,7 @@ bool QGstreamerPlayerSession::setPipeline(GstElement *pipeline) m_pipeline = pipeline; gst_object_unref(GST_OBJECT(m_bus)); m_bus = bus; - delete m_busHelper; + m_busHelper->deleteLater(); m_busHelper = new QGstreamerBusHelper(m_bus, this); m_busHelper->installMessageFilter(this); -- cgit v1.2.3 From 80cc653364fe330dfa4bf310f98d98d4cef0698b Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Mon, 20 May 2019 10:37:32 +0200 Subject: GStreamer: Dump dot file if GST_DEBUG_DUMP_DOT_DIR is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dump dot file on play only. Change-Id: Ide7fe0cad56f06a89604cf40b59b858c9c9d09f2 Reviewed-by: Christian Strømme --- src/gsttools/qgstreamerplayersession.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index 5c9eebca2..56f78cb9f 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -63,7 +63,6 @@ #include //#define DEBUG_PLAYBIN -//#define DEBUG_VO_BIN_DUMP QT_BEGIN_NAMESPACE @@ -659,12 +658,6 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) if (!m_playbin) return; -#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*/), - "playbin_set"); -#endif - GstElement *videoSink = 0; if (m_renderer && m_renderer->isReady()) videoSink = m_renderer->videoSink(); @@ -925,11 +918,6 @@ void QGstreamerPlayerSession::finishVideoOutputChange() 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 */), - "playbin_finish"); -#endif } #if !GST_CHECK_VERSION(1,0,0) @@ -992,6 +980,9 @@ bool QGstreamerPlayerSession::isSeekable() const bool QGstreamerPlayerSession::play() { + static bool dumpDot = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR"); + if (dumpDot) + gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_pipeline), GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL), "session.play"); #ifdef DEBUG_PLAYBIN qDebug() << Q_FUNC_INFO; #endif -- cgit v1.2.3 From 2b34e3355c8943f41c84f39ad9a838f6edb80429 Mon Sep 17 00:00:00 2001 From: VaL Doroshchuk Date: Thu, 2 May 2019 09:42:39 +0200 Subject: Gstreamer: Pass GstUDPSrc's caps via "udpsrc.caps=" url's query item MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it is needed to configure udpsrc element to play a stream, means to set some caps to GstUDPSrc element. But currently there are no any ways to pass such caps. Added parsing of the requested url to find "udpsrc.caps" query item and to use it as the caps for udpsrc source element. It allows to show streams by passing caps within url. E.g. if the stream is created using $ gst-launch-1.0 v4l2src ! videoconvert ! video/x-raw,format=I420,width=800,height=600 ! \ jpegenc ! rtpjpegpay ! udpsink host=127.0.0.1 port=5001 it could be shown via QMediaPlayer like: MediaPlayer { source: "udp://127.0.0.1:5001/?udpsrc.caps=application/x-rtp,media=video,clock-rate=90000,encoding=JPEG,payload=26" } Change-Id: I6f9c20c6004a34bce5fd1d0073311b7c62a8010f Reviewed-by: Christian Strømme --- src/gsttools/qgstreamerplayersession.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index 56f78cb9f..5ede8a1c9 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -61,6 +61,7 @@ #include #include #include +#include //#define DEBUG_PLAYBIN @@ -1658,6 +1659,14 @@ void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpo self->m_sourceType = UDPSrc; //The udpsrc is always a live source. self->m_isLiveSource = true; + + QUrlQuery query(self->m_request.url()); + const QString var = QLatin1String("udpsrc.caps"); + if (query.hasQueryItem(var)) { + GstCaps *caps = gst_caps_from_string(query.queryItemValue(var).toLatin1().constData()); + g_object_set(G_OBJECT(source), "caps", caps, NULL); + gst_caps_unref(caps); + } } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstSoupHTTPSrc") == 0) { //souphttpsrc timeout unit = second g_object_set(G_OBJECT(source), "timeout", guint(timeout), NULL); -- cgit v1.2.3 From 3bbeae539854c1474844c65025d40b813cec286c Mon Sep 17 00:00:00 2001 From: Val Doroshchuk Date: Thu, 11 Apr 2019 13:54:58 +0200 Subject: DirectShow: Don't hide returned error when video output was not provided Since the video output could be set after doRender(), we do not report an error in this case. But need to report the error when the video output has not been submitted at all. Otherwise it hides the error and not possible to determine playback issues. Added a fix to remember an error while rendering without the video output and handle it if playing is requested. That means, if it is required, the video output must be set before play() is called. Not all medias require valid video output, but if there is an error, we need to report about it. Fixes tst_QMediaPlayerBackend::playlist() Task-number: QTBUG-65574 Change-Id: I9faae19c08ad0273545bb7617ea3a11539084f1f Reviewed-by: Andy Shaw --- .../directshow/player/directshowplayerservice.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index 2eeb06159..78b4be35b 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -524,9 +524,16 @@ void DirectShowPlayerService::doRender(QMutexLocker *locker) } else { locker->unlock(); HRESULT hr = graph->RenderEx(pin, /*AM_RENDEREX_RENDERTOEXISTINGRENDERERS*/ 1, 0); - // Do not return an error if no video output is set yet. - if (SUCCEEDED(hr) || !(m_executedTasks & SetVideoOutput)) { + if (SUCCEEDED(hr)) { rendered = true; + m_error = QMediaPlayer::NoError; + } else if (!(m_executedTasks & SetVideoOutput)) { + // Do not return an error if no video output is set yet. + rendered = true; + // Remember the error in this case. + // Handle it when playing is requested and no video output has been provided. + m_error = QMediaPlayer::ResourceError; + m_errorString = QString("%1: %2").arg(__FUNCTION__).arg(qt_error_string(hr)); } else if (renderHr == S_OK || renderHr == VFW_E_NO_DECOMPRESSOR){ renderHr = hr; } @@ -918,6 +925,16 @@ void DirectShowPlayerService::play() void DirectShowPlayerService::doPlay(QMutexLocker *locker) { + // Invalidate if there is an error while loading. + if (m_error != QMediaPlayer::NoError) { + m_graphStatus = InvalidMedia; + if (!m_errorString.isEmpty()) + qWarning("%s", qPrintable(m_errorString)); + m_errorString = QString(); + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + return; + } + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { locker->unlock(); HRESULT hr = control->Run(); -- cgit v1.2.3