diff options
author | Lars Knoll <lars.knoll@qt.io> | 2021-09-24 10:53:15 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2021-10-01 08:55:26 +0000 |
commit | 23333fb4be2293ceaf129fec134ea687a30596d9 (patch) | |
tree | 077f772744c22eaf56e0375bf290e01580ee6d77 | |
parent | 0581294df9671ad67218ab0ae9e7d297137b4c89 (diff) |
Add support for looping to QMediaPlayer
Gapless looping is important for a couple of use cases where
you want to play back some file a couple of times.
Task-number: QTBUG-95010
Pick-to: 6.2
Change-Id: Idd089f0702acaf3cab71987656f017f4c7fa4c54
Reviewed-by: André de la Rocha <andre.rocha@qt.io>
Reviewed-by: Piotr Srebrny <piotr.srebrny@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
7 files changed, 98 insertions, 1 deletions
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp index db24b9f0b..ed1d329f1 100644 --- a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp +++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp @@ -380,6 +380,8 @@ void QAndroidMediaPlayer::play() { StateChangeNotifier notifier(this); + resetCurrentLoop(); + // We need to prepare the mediaplayer again. if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isEmpty()) { setMedia(mMediaContent, mMediaStream); @@ -612,6 +614,10 @@ void QAndroidMediaPlayer::onStateChanged(qint32 state) Q_EMIT positionChanged(0); break; case AndroidMediaPlayer::PlaybackCompleted: + if (doLoop()) { + mMediaPlayer->play(); + break; + } stateChanged(QMediaPlayer::StoppedState); setMediaStatus(QMediaPlayer::EndOfMedia); break; diff --git a/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer.mm b/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer.mm index 0b5e831ba..6b0514e6e 100644 --- a/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer.mm +++ b/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer.mm @@ -824,6 +824,8 @@ void AVFMediaPlayer::play() if (m_state == QMediaPlayer::PlayingState) return; + resetCurrentLoop(); + if (m_videoOutput && m_videoSink) m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]); @@ -933,6 +935,11 @@ void AVFMediaPlayer::audioOutputChanged() void AVFMediaPlayer::processEOS() { + if (doLoop()) { + [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate]; + return; + } + //AVPlayerItem has reached end of track/stream #ifdef QT_DEBUG_AVF qDebug() << Q_FUNC_INFO; diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp index ccfd7041f..e3327a85a 100644 --- a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp +++ b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp @@ -203,6 +203,7 @@ void QGstreamerMediaPlayer::play() { if (state() == QMediaPlayer::PlayingState || m_url.isEmpty()) return; + resetCurrentLoop(); *playerPipeline.inStoppedState() = false; if (mediaStatus() == QMediaPlayer::EndOfMedia) { @@ -297,6 +298,8 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message) return false; } case GST_MESSAGE_EOS: + if (doLoop()) + break; stopOrEOS(true); break; case GST_MESSAGE_BUFFERING: { diff --git a/src/multimedia/platform/qplatformmediaplayer_p.h b/src/multimedia/platform/qplatformmediaplayer_p.h index cc9297229..751720774 100644 --- a/src/multimedia/platform/qplatformmediaplayer_p.h +++ b/src/multimedia/platform/qplatformmediaplayer_p.h @@ -141,6 +141,24 @@ public: void mediaStatusChanged(QMediaPlayer::MediaStatus status); void error(int error, const QString &errorString); + void resetCurrentLoop() { m_currentLoop = 0; } + bool doLoop() { + if (!isSeekable()) + return false; + if (m_loops < 0 || ++m_currentLoop < m_loops) { + setPosition(0); + return true; + } + return false; + } + int loops() { return m_loops; } + void setLoops(int loops) { + if (m_loops == loops) + return; + m_loops = loops; + Q_EMIT player->loopsChanged(); + } + protected: explicit QPlatformMediaPlayer(QMediaPlayer *parent = nullptr) : player(parent) @@ -152,6 +170,8 @@ private: bool m_seekable = false; bool m_videoAvailable = false; bool m_audioAvailable = false; + int m_loops = 1; + int m_currentLoop = 0; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/player/mfplayercontrol.cpp b/src/multimedia/platform/windows/player/mfplayercontrol.cpp index d78768546..4e1046b05 100644 --- a/src/multimedia/platform/windows/player/mfplayercontrol.cpp +++ b/src/multimedia/platform/windows/player/mfplayercontrol.cpp @@ -82,6 +82,7 @@ void MFPlayerControl::play() { if (m_state == QMediaPlayer::PlayingState) return; + resetCurrentLoop(); if (QMediaPlayer::InvalidMedia == m_session->status()) m_session->load(m_media, m_stream); @@ -160,7 +161,10 @@ void MFPlayerControl::handleStatusChanged() QMediaPlayer::MediaStatus status = m_session->status(); switch (status) { case QMediaPlayer::EndOfMedia: - changeState(QMediaPlayer::StoppedState); + if (doLoop()) + m_session->start(); + else + changeState(QMediaPlayer::StoppedState); break; case QMediaPlayer::InvalidMedia: break; diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index 2fc4ad4df..d76f3fd0d 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -454,6 +454,51 @@ qreal QMediaPlayer::playbackRate() const } /*! + \enum QMediaPlayer::Loops + + Some predefined constants for the \l loops property. + + \value Infinite Loop forever. + \value Once Play the media once (the default). +*/ + +/*! + \property QMediaPlayer::loops + + Determines how often the media is played before the player stops. + Set to QMediaPlayer::Infinite to loop the current media file forever. + + The default value is \c 1. Setting this property to \c 0 has no effect. +*/ + +/*! + \qmlproperty int QtMultimedia::MediaPlayer::loops + + Determines how often the media is played before the player stops. + Set to MediaPlayer::Infinite to loop the current media file forever. + + The default value is \c 1. Setting this property to \c 0 has no effect. +*/ +int QMediaPlayer::loops() const +{ + Q_D(const QMediaPlayer); + + if (d->control) + return d->control->loops(); + + return 1; +} + +void QMediaPlayer::setLoops(int loops) +{ + Q_D(QMediaPlayer); + if (loops == 0) + return; + if (d->control) + d->control->setLoops(loops); +} + +/*! Returns the current error state. */ QMediaPlayer::Error QMediaPlayer::error() const diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h index 5796c89e6..0ccb0eaaf 100644 --- a/src/multimedia/playback/qmediaplayer.h +++ b/src/multimedia/playback/qmediaplayer.h @@ -66,6 +66,7 @@ class Q_MULTIMEDIA_EXPORT QMediaPlayer : public QObject Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged) Q_PROPERTY(bool seekable READ isSeekable NOTIFY seekableChanged) Q_PROPERTY(qreal playbackRate READ playbackRate WRITE setPlaybackRate NOTIFY playbackRateChanged) + Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged) Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged) Q_PROPERTY(MediaStatus mediaStatus READ mediaStatus NOTIFY mediaStatusChanged) Q_PROPERTY(QMediaMetaData metaData READ metaData NOTIFY metaDataChanged) @@ -118,6 +119,13 @@ public: }; Q_ENUM(Error) + enum Loops + { + Infinite = -1, + Once = 1 + }; + Q_ENUM(Loops) + explicit QMediaPlayer(QObject *parent = nullptr); ~QMediaPlayer(); @@ -163,6 +171,9 @@ public: bool isSeekable() const; qreal playbackRate() const; + int loops() const; + void setLoops(int loops); + Error error() const; QString errorString() const; @@ -196,6 +207,7 @@ Q_SIGNALS: void seekableChanged(bool seekable); void playbackRateChanged(qreal rate); + void loopsChanged(); void metaDataChanged(); void videoOutputChanged(); |