diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-06-03 15:34:21 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-06-03 15:34:21 +0200 |
commit | ba3a161c41eba3148d66bc5123707fcf613e2bcb (patch) | |
tree | a092b1400a82b86acee4e2771bfc2f27630f0261 | |
parent | afca8928ce8e4113a224e57fa30a3b4bc5a2db47 (diff) | |
parent | 58a304824abbff8f1bf3d5031c1799de91869190 (diff) | |
download | qtmultimedia-ba3a161c41eba3148d66bc5123707fcf613e2bcb.tar.gz |
Merge "Merge remote-tracking branch 'origin/5.13' into dev"
-rw-r--r-- | src/gsttools/qgstreamerplayersession.cpp | 136 | ||||
-rw-r--r-- | src/gsttools/qgstreamerplayersession_p.h | 3 | ||||
-rw-r--r-- | src/gsttools/qgstvideorenderersink.cpp | 3 | ||||
-rw-r--r-- | src/multimediawidgets/qpaintervideosurface.cpp | 25 | ||||
-rw-r--r-- | src/plugins/common/evr/evrd3dpresentengine.cpp | 10 | ||||
-rw-r--r-- | src/plugins/directshow/player/directshowplayercontrol.cpp | 3 | ||||
-rw-r--r-- | src/plugins/directshow/player/directshowplayerservice.cpp | 25 | ||||
-rw-r--r-- | src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp | 5 |
8 files changed, 130 insertions, 80 deletions
diff --git a/src/gsttools/qgstreamerplayersession.cpp b/src/gsttools/qgstreamerplayersession.cpp index 24f9b788b..2f89d2a1a 100644 --- a/src/gsttools/qgstreamerplayersession.cpp +++ b/src/gsttools/qgstreamerplayersession.cpp @@ -61,9 +61,9 @@ #include <QtCore/qdir.h> #include <QtCore/qstandardpaths.h> #include <qvideorenderercontrol.h> +#include <QUrlQuery> //#define DEBUG_PLAYBIN -//#define DEBUG_VO_BIN_DUMP QT_BEGIN_NAMESPACE @@ -319,7 +319,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(); @@ -351,28 +351,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<QVideoRendererControl*>(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(); @@ -387,17 +366,61 @@ 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<QVideoRendererControl *>(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<bool(GstElement *)> &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<GstElement*>(g_value_get_object(&value)); +#else + GstElement *child = nullptr; + while (gst_iterator_next(it, reinterpret_cast<gpointer *>(&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; 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); @@ -414,24 +437,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<GstElement*>(g_value_get_object(&data)); -#else - GstElement *child = nullptr; - while (gst_iterator_next(it, reinterpret_cast<gpointer *>(&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 @@ -642,12 +672,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(); @@ -908,11 +932,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) @@ -975,6 +994,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 @@ -1650,6 +1672,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); diff --git a/src/gsttools/qgstreamerplayersession_p.h b/src/gsttools/qgstreamerplayersession_p.h index b613793c4..d4b050272 100644 --- a/src/gsttools/qgstreamerplayersession_p.h +++ b/src/gsttools/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; diff --git a/src/gsttools/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp index 49016b952..5f71d342c 100644 --- a/src/gsttools/qgstvideorenderersink.cpp +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -686,7 +686,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. 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 <QtDebug> 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<QObject*>(m_glContext->contextHandle())); - m_glContext->makeCurrent(); + makeCurrent(m_glContext); const QByteArray extensions(reinterpret_cast<const char *>( 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: 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; } 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; diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index 059e253f1..d5b8bef26 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -408,8 +408,6 @@ void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker) m_pendingTasks |= SetRate; m_source = source; - } else if (!m_url.isEmpty()) { - m_pendingTasks |= SetUrlSource; } else { m_graphStatus = InvalidMedia; @@ -531,9 +529,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; } @@ -914,6 +919,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<IMediaControl>(m_graph, IID_IMediaControl)) { locker->unlock(); HRESULT hr = control->Run(); @@ -1615,7 +1630,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 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<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode) QList<QByteArray> 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<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode) } pDevEnum->Release(); } - CoUninitialize(); + if (SUCCEEDED(hrCoInit)) + CoUninitialize(); return devices; } |