diff options
-rw-r--r-- | src/imports/multimedia/qmldir | 1 | ||||
-rw-r--r-- | src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp | 5 | ||||
-rw-r--r-- | src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h | 1 | ||||
-rw-r--r-- | src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp | 32 | ||||
-rw-r--r-- | src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h | 5 | ||||
-rw-r--r-- | src/multimedia/platform/qplatformmediaplayer_p.h | 1 | ||||
-rw-r--r-- | src/multimedia/playback/qmediaplayer.cpp | 17 | ||||
-rw-r--r-- | src/multimedia/playback/qmediaplayer.h | 3 | ||||
-rw-r--r-- | src/multimedia/video/qvideosink.cpp | 62 | ||||
-rw-r--r-- | src/multimedia/video/qvideosink.h | 8 | ||||
-rw-r--r-- | src/multimediawidgets/qvideowidget.cpp | 20 | ||||
-rw-r--r-- | src/multimediawidgets/qvideowidget.h | 6 | ||||
-rw-r--r-- | src/multimediawidgets/qvideowidget_p.h | 4 |
13 files changed, 154 insertions, 11 deletions
diff --git a/src/imports/multimedia/qmldir b/src/imports/multimedia/qmldir index e7dcc3dd6..48cdb0c8c 100644 --- a/src/imports/multimedia/qmldir +++ b/src/imports/multimedia/qmldir @@ -2,6 +2,7 @@ module QtMultimedia plugin declarative_multimedia classname QMultimediaDeclarativeModule typeinfo plugins.qmltypes +prefer :/qt-project.org/imports/QtMultimedia/ typeinfo plugins.qmltypes Video 5.0 Video.qml diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp index f7043c7f9..930dec8d1 100644 --- a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp +++ b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp @@ -605,6 +605,11 @@ QMediaMetaData QGstreamerMediaPlayer::metaData() const return m_metaData; } +void QGstreamerMediaPlayer::setVideoSink(QVideoSink *sink) +{ + gstVideoOutput->setVideoSink(sink); +} + void QGstreamerMediaPlayer::setSeekable(bool seekable) { qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << seekable; diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h index 319059ca1..fcaf3f6aa 100644 --- a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h +++ b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h @@ -108,6 +108,7 @@ public: QMediaMetaData metaData() const override; + void setVideoSink(QVideoSink *sink) override; void setVideoSurface(QAbstractVideoSurface *surface) override; int trackCount(TrackType) override; diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp index 939e88599..103a15557 100644 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp +++ b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp @@ -39,6 +39,8 @@ #include <private/qgstreamervideooutput_p.h> #include <private/qgstreamervideorenderer_p.h> +#include <private/qgstreamervideowindow_p.h> +#include <qvideosink.h> #include <QtCore/qloggingcategory.h> #include <qthread.h> @@ -109,6 +111,36 @@ void QGstreamerVideoOutput::setVideoSurface(QAbstractVideoSurface *surface) pad.addProbe<&QGstreamerVideoOutput::prepareVideoOutputChange>(this, GST_PAD_PROBE_TYPE_IDLE); } +void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink) +{ + if (!m_videoWindow) { + m_videoWindow = new QGstreamerVideoWindow; + gstPipeline.installMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(m_videoWindow)); + gstPipeline.installMessageFilter(static_cast<QGstreamerBusMessageFilter *>(m_videoWindow)); + m_videoWindow->setWinId(sink->nativeWindowId()); + } + + newVideoSink = m_videoWindow->videoSink(); + if (newVideoSink == videoSink) { + newVideoSink = {}; + return; + } + gstVideoOutput.add(newVideoSink); + + qCDebug(qLcMediaVideoOutput) << "setVideoSurface: Reconfiguring video output" << QThread::currentThreadId(); + + auto state = gstPipeline.state(); + + if (state != GST_STATE_PLAYING) { + changeVideoOutput(); + return; + } + + // This doesn't quite work, as we're be getting the callback in another thread where state changes aren't allowed. + auto pad = videoScale.staticPad("src"); + pad.addProbe<&QGstreamerVideoOutput::prepareVideoOutputChange>(this, GST_PAD_PROBE_TYPE_IDLE); +} + void QGstreamerVideoOutput::setIsPreview() { // configures the queue to be fast and lightweight for camera preview diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h index d2612e76f..fa6ec3fad 100644 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h +++ b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE class QGstreamerVideoRenderer; class QAbstractVideoSurface; +class QVideoSink; +class QGstreamerVideoWindow; class Q_MULTIMEDIA_EXPORT QGstreamerVideoOutput : public QObject { @@ -69,6 +71,7 @@ public: ~QGstreamerVideoOutput(); void setVideoSurface(QAbstractVideoSurface *surface); + void setVideoSink(QVideoSink *sink); void setPipeline(const QGstPipeline &pipeline) { gstPipeline = pipeline; } @@ -81,7 +84,9 @@ private: void changeVideoOutput(); QAbstractVideoSurface *m_videoSurface = nullptr; + QVideoSink *m_videoSink = nullptr; QGstreamerVideoRenderer *m_videoOutput = nullptr; + QGstreamerVideoWindow *m_videoWindow = nullptr; // Gst elements QGstPipeline gstPipeline; diff --git a/src/multimedia/platform/qplatformmediaplayer_p.h b/src/multimedia/platform/qplatformmediaplayer_p.h index e34a8b22b..832caf859 100644 --- a/src/multimedia/platform/qplatformmediaplayer_p.h +++ b/src/multimedia/platform/qplatformmediaplayer_p.h @@ -115,6 +115,7 @@ public: virtual QMediaMetaData metaData() const { return {}; } virtual void setVideoSurface(QAbstractVideoSurface *surface) = 0; + virtual void setVideoSink(QVideoSink */*sink*/) {} // media streams enum TrackType { VideoStream, AudioStream, SubtitleStream, NTrackTypes }; diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index 2c196a04d..5917b083e 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -714,6 +714,11 @@ void QMediaPlayer::setActiveSubtitleTrack(int index) void QMediaPlayer::setVideoOutput(QObject *output) { auto *mo = output->metaObject(); + QVideoSink *sink = nullptr; + if (!output || mo->invokeMethod(output, "videoSink", Q_RETURN_ARG(QVideoSink *, sink))) { + setVideoOutput(sink); + return; + } QAbstractVideoSurface *surface = nullptr; if (output && !mo->invokeMethod(output, "videoSurface", Q_RETURN_ARG(QAbstractVideoSurface *, surface))) { qWarning() << "QMediaPlayer::setVideoOutput: Object" << output->metaObject()->className() << "does not have a videoSurface()"; @@ -728,7 +733,6 @@ void QMediaPlayer::setVideoOutput(QObject *output) If a video output has already been set on the media player the new surface will replace it. */ - void QMediaPlayer::setVideoOutput(QAbstractVideoSurface *surface) { Q_D(QMediaPlayer); @@ -739,6 +743,16 @@ void QMediaPlayer::setVideoOutput(QAbstractVideoSurface *surface) d->control->setVideoSurface(surface); } +void QMediaPlayer::setVideoOutput(QVideoSink *sink) +{ + Q_D(QMediaPlayer); + + if (!d->control) + return; + + d->control->setVideoSink(sink); +} + /*! \since 5.15 Sets multiple video surfaces as the video output of a media player. @@ -751,7 +765,6 @@ void QMediaPlayer::setVideoOutput(QAbstractVideoSurface *surface) \sa QAbstractVideoSurface::supportedPixelFormats */ - void QMediaPlayer::setVideoOutput(const QList<QAbstractVideoSurface *> &surfaces) { setVideoOutput(!surfaces.empty() ? new QVideoSurfaces(surfaces, this) : nullptr); diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h index 38429ecb7..c52bea22f 100644 --- a/src/multimedia/playback/qmediaplayer.h +++ b/src/multimedia/playback/qmediaplayer.h @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE - +class QVideoSink; class QAbstractVideoSurface; class QAudioDeviceInfo; class QMediaMetaData; @@ -138,6 +138,7 @@ public: void setVideoOutput(QObject *); void setVideoOutput(QAbstractVideoSurface *surface); void setVideoOutput(const QList<QAbstractVideoSurface *> &surfaces); + void setVideoOutput(QVideoSink *sink); QUrl media() const; const QIODevice *mediaStream() const; diff --git a/src/multimedia/video/qvideosink.cpp b/src/multimedia/video/qvideosink.cpp index de619eab8..77ee54593 100644 --- a/src/multimedia/video/qvideosink.cpp +++ b/src/multimedia/video/qvideosink.cpp @@ -88,6 +88,12 @@ void QVideoSink::setGraphicsType(QVideoSink::GraphicsType type) d->type = type; } +bool QVideoSink::isGraphicsTypeSupported(QVideoSink::GraphicsType type) +{ + // #### + return type == NativeWindow; +} + WId QVideoSink::nativeWindowId() const { return d->window; @@ -184,11 +190,57 @@ void QVideoSink::render(const QVideoFrame &frame) } -void QVideoSink::paint(QPainter *painter, const QVideoFrame &frame) -{ - Q_UNUSED(painter); - Q_UNUSED(frame); - +void QVideoSink::paint(QPainter *painter, const QVideoFrame &f) +{ + QVideoFrame frame(f); + if (!frame.isValid()) { + painter->fillRect(d->targetRect, Qt::black); + return; + } + + auto imageFormat = QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()); + // Do not render into ARGB32 images using QPainter. + // Using QImage::Format_ARGB32_Premultiplied is significantly faster. + if (imageFormat == QImage::Format_ARGB32) + imageFormat = QImage::Format_ARGB32_Premultiplied; + + QVideoSurfaceFormat::Direction scanLineDirection = QVideoSurfaceFormat::TopToBottom;//format.scanLineDirection(); + bool mirrored = false;//format.isMirrored(); + + QRectF source = d->targetRect; // #### + + if (frame.handleType() == QVideoFrame::QPixmapHandle) { + painter->drawPixmap(d->targetRect, frame.handle().value<QPixmap>(), source); + } else if (frame.map(QVideoFrame::ReadOnly)) { + QImage image = frame.image(); + + auto oldOpacity = painter->opacity(); + const QTransform oldTransform = painter->transform(); + QTransform transform = oldTransform; + QRectF targetRect = d->targetRect; + if (scanLineDirection == QVideoSurfaceFormat::BottomToTop) { + transform.scale(1, -1); + transform.translate(0, -targetRect.bottom()); + targetRect = QRectF(targetRect.x(), 0, targetRect.width(), targetRect.height()); + } + + if (mirrored) { + transform.scale(-1, 1); + transform.translate(-targetRect.right(), 0); + targetRect = QRectF(0, targetRect.y(), targetRect.width(), targetRect.height()); + } + painter->setTransform(transform); + painter->setOpacity(d->opacity); + painter->drawImage(targetRect, image, source); + painter->setTransform(oldTransform); + painter->setOpacity(oldOpacity); + + frame.unmap(); + } else if (frame.isValid()) { + // #### error handling + } else { + painter->fillRect(targetRect(), Qt::black); + } } QT_END_NAMESPACE diff --git a/src/multimedia/video/qvideosink.h b/src/multimedia/video/qvideosink.h index e4ae146c0..2c7da355b 100644 --- a/src/multimedia/video/qvideosink.h +++ b/src/multimedia/video/qvideosink.h @@ -40,6 +40,7 @@ #ifndef QABSTRACTVIDEOSINK_H #define QABSTRACTVIDEOSINK_H +#include <QtMultimedia/qtmultimediaglobal.h> #include <QtCore/qobject.h> #include <QtGui/qwindowdefs.h> @@ -51,7 +52,7 @@ class QVideoFrame; class QVideoSinkPrivate; -class QVideoSink : public QObject +class Q_MULTIMEDIA_EXPORT QVideoSink : public QObject { Q_OBJECT public: @@ -59,6 +60,7 @@ public: { Memory, NativeWindow, + NativeTexture, OpenGL, Metal, Direct3D11, @@ -71,6 +73,8 @@ public: GraphicsType graphicsType() const; void setGraphicsType(GraphicsType type); + static bool isGraphicsTypeSupported(GraphicsType type); + // setter sets graphics type to NativeWindow WId nativeWindowId() const; void setNativeWindowId(WId id); @@ -108,7 +112,7 @@ public: Q_SIGNALS: // would never get called in windowed mode - QVideoFrame newVideoFrame() const; + QVideoFrame newVideoFrame(const QVideoFrame &frame) const; private: QVideoSinkPrivate *d = nullptr; diff --git a/src/multimediawidgets/qvideowidget.cpp b/src/multimediawidgets/qvideowidget.cpp index 6dc019378..a739f8e96 100644 --- a/src/multimediawidgets/qvideowidget.cpp +++ b/src/multimediawidgets/qvideowidget.cpp @@ -43,6 +43,7 @@ #include <QtCore/qobject.h> #include <QtMultimedia/qtmultimediaglobal.h> #include <qvideowindowcontrol.h> +#include <qvideosink.h> #include <qobject.h> #include <qvideosurfaceformat.h> @@ -287,6 +288,12 @@ void QVideoWidgetPrivate::_q_dimensionsChanged() q_func()->update(); } +void QVideoWidgetPrivate::_q_newFrame(const QVideoFrame &frame) +{ + lastFrame = frame; + q_ptr->update(); +} + /*! \class QVideoWidget @@ -317,6 +324,9 @@ QVideoWidget::QVideoWidget(QWidget *parent) , d_ptr(new QVideoWidgetPrivate) { d_ptr->q_ptr = this; + d_ptr->videoSink = new QVideoSink(this); + d_ptr->videoSink->setGraphicsType(QVideoSink::NativeWindow); + d_ptr->videoSink->setNativeWindowId(winId()); } /*! @@ -362,6 +372,11 @@ QAbstractVideoSurface *QVideoWidget::videoSurface() const return d->backend->videoSurface(); } +QVideoSink *QVideoWidget::videoSink() const +{ + return d_ptr->videoSink; +} + /*! \property QVideoWidget::aspectRatioMode \brief how video is scaled with respect to its aspect ratio. @@ -652,6 +667,11 @@ void QVideoWidget::paintEvent(QPaintEvent *event) { Q_D(QVideoWidget); + if (d->videoSink && d->lastFrame.isValid()) { + QPainter painter(this); + d->videoSink->paint(&painter, d->lastFrame); + return; + } if (d->backend) { d->backend->paintEvent(event); } else if (testAttribute(Qt::WA_OpaquePaintEvent)) { diff --git a/src/multimediawidgets/qvideowidget.h b/src/multimediawidgets/qvideowidget.h index 866e5fef4..68e865ddd 100644 --- a/src/multimediawidgets/qvideowidget.h +++ b/src/multimediawidgets/qvideowidget.h @@ -46,8 +46,10 @@ QT_BEGIN_NAMESPACE -class QVideoWidgetPrivate; class QAbstractVideoSurface; +class QVideoSink; + +class QVideoWidgetPrivate; class Q_MULTIMEDIAWIDGETS_EXPORT QVideoWidget : public QWidget { Q_OBJECT @@ -64,6 +66,7 @@ public: ~QVideoWidget(); Q_INVOKABLE QAbstractVideoSurface *videoSurface() const; + Q_INVOKABLE QVideoSink *videoSink() const; #ifdef Q_QDOC bool isFullScreen() const; @@ -115,6 +118,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_saturationChanged(int)) Q_PRIVATE_SLOT(d_func(), void _q_fullScreenChanged(bool)) Q_PRIVATE_SLOT(d_func(), void _q_dimensionsChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_newFrame(const QVideoFrame &)) }; QT_END_NAMESPACE diff --git a/src/multimediawidgets/qvideowidget_p.h b/src/multimediawidgets/qvideowidget_p.h index 52a93e99c..9ced54c8f 100644 --- a/src/multimediawidgets/qvideowidget_p.h +++ b/src/multimediawidgets/qvideowidget_p.h @@ -136,6 +136,9 @@ public: Qt::WindowFlags nonFullScreenFlags; bool wasFullScreen = false; + QVideoFrame lastFrame; + QVideoSink *videoSink = nullptr; + bool createBackend(); void _q_brightnessChanged(int brightness); @@ -144,6 +147,7 @@ public: void _q_saturationChanged(int saturation); void _q_fullScreenChanged(bool fullScreen); void _q_dimensionsChanged(); + void _q_newFrame(const QVideoFrame &frame); }; QT_END_NAMESPACE |