Android: fix some problems with the media player.
authorYoann Lopes <yoann.lopes@digia.com>
Fri, 19 Sep 2014 07:53:53 +0000 (09:53 +0200)
committerYoann Lopes <yoann.lopes@digia.com>
Fri, 26 Sep 2014 12:26:58 +0000 (14:26 +0200)
- Correctly emit positionChanged signal. One of the problems with
  this was that QMediaPlayer automatically sends periodic
  position updates while playing a media. There's no need to have
  the same logic in the backend.
- Seeking after reaching the end of the media now correctly works

Auto tests included.

Change-Id: I6d5ecbae6e05f94a8aac1a0834cf57427adf219b
Reviewed-by: Christian Stromme <christian.stromme@digia.com>
src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java
src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp

index ade2517..5e6630d 100644 (file)
@@ -83,23 +83,6 @@ public class QtAndroidMediaPlayer
 
     private volatile int mState = State.Uninitialized;
 
-    private class ProgressWatcher
-    implements Runnable
-    {
-        @Override
-        public void run()
-        {
-            try {
-                while ((mState & (State.Started)) != 0) {
-                    onProgressUpdateNative(getCurrentPosition(), mID);
-                    Thread.sleep(1000);
-                }
-            } catch (final InterruptedException e) {
-                // Ignore
-            }
-        }
-    }
-
     /**
      * MediaPlayer OnErrorListener
      */
@@ -257,8 +240,6 @@ public class QtAndroidMediaPlayer
         try {
             mMediaPlayer.start();
             setState(State.Started);
-            Thread progressThread = new Thread(new ProgressWatcher());
-            progressThread.start();
         } catch (final IllegalStateException e) {
             Log.d(TAG, "" + e.getMessage());
         }
@@ -309,7 +290,6 @@ public class QtAndroidMediaPlayer
 
         try {
             mMediaPlayer.seekTo(msec);
-            onProgressUpdateNative(msec, mID);
         } catch (final IllegalStateException e) {
             Log.d(TAG, "" + e.getMessage());
         }
index fb1c8b7..eae09c6 100644 (file)
@@ -86,6 +86,7 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
       mPendingSetMedia(false),
       mPendingVolume(-1),
       mPendingMute(-1),
+      mReloadingMedia(false),
       mActiveStateChangeNotifiers(0)
 {
     connect(mMediaPlayer,SIGNAL(bufferingChanged(qint32)),
@@ -138,17 +139,14 @@ qint64 QAndroidMediaPlayerControl::position() const
     if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
         return duration();
 
-    if ((mState & (AndroidMediaPlayer::Idle
-                   | AndroidMediaPlayer::Initialized
-                   | AndroidMediaPlayer::Prepared
+    if ((mState & (AndroidMediaPlayer::Prepared
                    | AndroidMediaPlayer::Started
                    | AndroidMediaPlayer::Paused
-                   | AndroidMediaPlayer::Stopped
-                   | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
-         return (mPendingPosition == -1) ? 0 : mPendingPosition;
+                   | AndroidMediaPlayer::PlaybackCompleted))) {
+        return mMediaPlayer->getCurrentPosition();
     }
 
-    return (mCurrentState == QMediaPlayer::StoppedState) ? 0 : mMediaPlayer->getCurrentPosition();
+    return (mPendingPosition == -1) ? 0 : mPendingPosition;
 }
 
 void QAndroidMediaPlayerControl::setPosition(qint64 position)
@@ -158,26 +156,25 @@ void QAndroidMediaPlayerControl::setPosition(qint64 position)
 
     const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
 
-    if ((mState & (AndroidMediaPlayer::Prepared
-                   | AndroidMediaPlayer::Started
-                   | AndroidMediaPlayer::Paused
-                   | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
-        if (mPendingPosition != seekPosition) {
-            mPendingPosition = seekPosition;
-            Q_EMIT positionChanged(seekPosition);
-        }
+    if (seekPosition == this->position())
         return;
-    }
 
     StateChangeNotifier notifier(this);
 
     if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
         setMediaStatus(QMediaPlayer::LoadedMedia);
 
-    mMediaPlayer->seekTo(seekPosition);
+    if ((mState & (AndroidMediaPlayer::Prepared
+                   | AndroidMediaPlayer::Started
+                   | AndroidMediaPlayer::Paused
+                   | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+        mPendingPosition = seekPosition;
+    } else {
+        mMediaPlayer->seekTo(seekPosition);
 
-    if (mPendingPosition != -1) {
-        mPendingPosition = -1;
+        if (mPendingPosition != -1) {
+            mPendingPosition = -1;
+        }
     }
 
     Q_EMIT positionChanged(seekPosition);
@@ -310,9 +307,9 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
 {
     StateChangeNotifier notifier(this);
 
-    const bool reloading = (mMediaContent == mediaContent);
+    mReloadingMedia = (mMediaContent == mediaContent);
 
-    if (!reloading) {
+    if (!mReloadingMedia) {
         mMediaContent = mediaContent;
         mMediaStream = stream;
     }
@@ -321,43 +318,45 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
     if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0)
         mMediaPlayer->release();
 
+    QString mediaPath;
+
     if (mediaContent.isNull()) {
         setMediaStatus(QMediaPlayer::NoMedia);
-        return;
-    }
-
-    if (mVideoOutput && !mVideoOutput->isReady()) {
-        // if a video output is set but the video texture is not ready, delay loading the media
-        // since it can cause problems on some hardware
-        mPendingSetMedia = true;
-        return;
-    }
-
-    const QUrl url = mediaContent.canonicalUrl();
-    QString mediaPath;
-    if (url.scheme() == QLatin1String("qrc")) {
-        const QString path = url.toString().mid(3);
-        mTempFile.reset(QTemporaryFile::createNativeFile(path));
-        if (!mTempFile.isNull())
-            mediaPath = QStringLiteral("file://") + mTempFile->fileName();
     } else {
-        mediaPath = url.toString();
-    }
+        if (mVideoOutput && !mVideoOutput->isReady()) {
+            // if a video output is set but the video texture is not ready, delay loading the media
+            // since it can cause problems on some hardware
+            mPendingSetMedia = true;
+            return;
+        }
 
-    if (mVideoSize.isValid() && mVideoOutput)
-        mVideoOutput->setVideoSize(mVideoSize);
+        const QUrl url = mediaContent.canonicalUrl();
+        if (url.scheme() == QLatin1String("qrc")) {
+            const QString path = url.toString().mid(3);
+            mTempFile.reset(QTemporaryFile::createNativeFile(path));
+            if (!mTempFile.isNull())
+                mediaPath = QStringLiteral("file://") + mTempFile->fileName();
+        } else {
+            mediaPath = url.toString();
+        }
 
-    if ((mMediaPlayer->display() == 0) && mVideoOutput)
-        mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
-    mMediaPlayer->setDataSource(mediaPath);
-    mMediaPlayer->prepareAsync();
+        if (mVideoSize.isValid() && mVideoOutput)
+            mVideoOutput->setVideoSize(mVideoSize);
+
+        if ((mMediaPlayer->display() == 0) && mVideoOutput)
+            mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+        mMediaPlayer->setDataSource(mediaPath);
+        mMediaPlayer->prepareAsync();
+    }
 
-    if (!reloading) {
+    if (!mReloadingMedia) {
         Q_EMIT mediaChanged(mMediaContent);
         Q_EMIT actualMediaLocationChanged(mediaPath);
     }
 
     resetBufferingProgress();
+
+    mReloadingMedia = false;
 }
 
 void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput)
@@ -567,7 +566,8 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
     case AndroidMediaPlayer::Initialized:
         break;
     case AndroidMediaPlayer::Preparing:
-        setMediaStatus(QMediaPlayer::LoadingMedia);
+        if (!mReloadingMedia)
+            setMediaStatus(QMediaPlayer::LoadingMedia);
         break;
     case AndroidMediaPlayer::Prepared:
         setMediaStatus(QMediaPlayer::LoadedMedia);
@@ -588,6 +588,7 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
         } else {
             setMediaStatus(QMediaPlayer::BufferedMedia);
         }
+        Q_EMIT positionChanged(position());
         break;
     case AndroidMediaPlayer::Paused:
         setState(QMediaPlayer::PausedState);
@@ -596,27 +597,32 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state)
         setState(QMediaPlayer::StoppedState);
         setMediaStatus(QMediaPlayer::UnknownMediaStatus);
         mMediaPlayer->release();
+        Q_EMIT positionChanged(0);
         break;
     case AndroidMediaPlayer::Stopped:
         setState(QMediaPlayer::StoppedState);
         setMediaStatus(QMediaPlayer::LoadedMedia);
-        setPosition(0);
+        Q_EMIT positionChanged(0);
         break;
     case AndroidMediaPlayer::PlaybackCompleted:
         setState(QMediaPlayer::StoppedState);
-        setPosition(0);
         setMediaStatus(QMediaPlayer::EndOfMedia);
         break;
     case AndroidMediaPlayer::Uninitialized:
-        // reset some properties
-        resetBufferingProgress();
-        mPendingPosition = -1;
-        mPendingSetMedia = false;
-        mPendingState = -1;
-
-        setAudioAvailable(false);
-        setVideoAvailable(false);
-        setSeekable(true);
+        // reset some properties (unless we reload the same media)
+        if (!mReloadingMedia) {
+            resetBufferingProgress();
+            mPendingPosition = -1;
+            mPendingSetMedia = false;
+            mPendingState = -1;
+
+            Q_EMIT durationChanged(0);
+            Q_EMIT positionChanged(0);
+
+            setAudioAvailable(false);
+            setVideoAvailable(false);
+            setSeekable(true);
+        }
         break;
     default:
         break;
@@ -655,13 +661,13 @@ void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status
     if (mCurrentMediaStatus == status)
         return;
 
+    mCurrentMediaStatus = status;
+
     if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
         Q_EMIT durationChanged(0);
 
     if (status == QMediaPlayer::EndOfMedia)
-        Q_EMIT durationChanged(duration());
-
-    mCurrentMediaStatus = status;
+        Q_EMIT positionChanged(position());
 
     updateBufferStatus();
 }
index 7de0c2d..64b88f4 100644 (file)
@@ -111,6 +111,7 @@ private:
     bool mPendingSetMedia;
     int mPendingVolume;
     int mPendingMute;
+    bool mReloadingMedia;
     QScopedPointer<QTemporaryFile> mTempFile;
     int mActiveStateChangeNotifiers;
 
index 5109d30..b4e2f1f 100644 (file)
@@ -71,6 +71,7 @@ private slots:
     void volumeAcrossFiles();
     void initialVolume();
     void seekPauseSeek();
+    void seekInStoppedState();
     void subsequentPlayback();
     void probes();
     void playlist();
@@ -79,10 +80,11 @@ private slots:
 
 private:
     QMediaContent selectVideoFile(const QStringList& mediaCandidates);
-    QMediaContent selectSoundFile(const QStringList& mediaCandidates);
+    QMediaContent selectMediaFile(const QStringList& mediaCandidates);
 
     //one second local wav file
     QMediaContent localWavFile;
+    QMediaContent localWavFile2;
     QMediaContent localVideoFile;
     QMediaContent localCompressedSoundFile;
 
@@ -169,17 +171,17 @@ QMediaContent tst_QMediaPlayerBackend::selectVideoFile(const QStringList& mediaC
     return QMediaContent();
 }
 
-QMediaContent tst_QMediaPlayerBackend::selectSoundFile(const QStringList& mediaCandidates)
+QMediaContent tst_QMediaPlayerBackend::selectMediaFile(const QStringList& mediaCandidates)
 {
     QMediaPlayer player;
 
     QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error)));
 
     foreach (QString s, mediaCandidates) {
-        QFileInfo soundFile(s);
-        if (!soundFile.exists())
+        QFileInfo mediaFile(s);
+        if (!mediaFile.exists())
             continue;
-        QMediaContent media = QMediaContent(QUrl::fromLocalFile(soundFile.absoluteFilePath()));
+        QMediaContent media = QMediaContent(QUrl::fromLocalFile(mediaFile.absoluteFilePath()));
         player.setMedia(media);
         player.play();
 
@@ -205,17 +207,24 @@ void tst_QMediaPlayerBackend::initTestCase()
 
     localWavFile = QMediaContent(QUrl::fromLocalFile(wavFile.absoluteFilePath()));
 
+    const QString testFileName2 = QFINDTESTDATA("testdata/_test.wav");
+    QFileInfo wavFile2(testFileName2);
+
+    QVERIFY(wavFile2.exists());
+
+    localWavFile2 = QMediaContent(QUrl::fromLocalFile(wavFile2.absoluteFilePath()));
+
     qRegisterMetaType<QMediaContent>();
 
     QStringList mediaCandidates;
     mediaCandidates << QFINDTESTDATA("testdata/colors.ogv");
     mediaCandidates << QFINDTESTDATA("testdata/colors.mp4");
-    localVideoFile = selectVideoFile(mediaCandidates);
+    localVideoFile = selectMediaFile(mediaCandidates);
 
     mediaCandidates.clear();
     mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mkv");
     mediaCandidates << QFINDTESTDATA("testdata/nokia-tune.mp3");
-    localCompressedSoundFile = selectSoundFile(mediaCandidates);
+    localCompressedSoundFile = selectMediaFile(mediaCandidates);
 
     qgetenv("QT_TEST_CI").toInt(&m_inCISystem,10);
 }
@@ -239,6 +248,7 @@ void tst_QMediaPlayerBackend::loadMedia()
     QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
     QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
     QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent)));
+    QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent)));
 
     player.setMedia(localWavFile);
 
@@ -247,11 +257,13 @@ void tst_QMediaPlayerBackend::loadMedia()
     QVERIFY(player.mediaStatus() != QMediaPlayer::NoMedia);
     QVERIFY(player.mediaStatus() != QMediaPlayer::InvalidMedia);
     QVERIFY(player.media() == localWavFile);
+    QVERIFY(player.currentMedia() == localWavFile);
 
     QCOMPARE(stateSpy.count(), 0);
     QVERIFY(statusSpy.count() > 0);
     QCOMPARE(mediaSpy.count(), 1);
     QCOMPARE(mediaSpy.last()[0].value<QMediaContent>(), localWavFile);
+    QCOMPARE(currentMediaSpy.last()[0].value<QMediaContent>(), localWavFile);
 
     QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
 
@@ -267,6 +279,7 @@ void tst_QMediaPlayerBackend::unloadMedia()
     QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
     QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
     QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent)));
+    QSignalSpy currentMediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent)));
     QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
     QSignalSpy durationSpy(&player, SIGNAL(positionChanged(qint64)));
 
@@ -285,6 +298,7 @@ void tst_QMediaPlayerBackend::unloadMedia()
     stateSpy.clear();
     statusSpy.clear();
     mediaSpy.clear();
+    currentMediaSpy.clear();
     positionSpy.clear();
     durationSpy.clear();
 
@@ -295,10 +309,12 @@ void tst_QMediaPlayerBackend::unloadMedia()
     QCOMPARE(player.state(), QMediaPlayer::StoppedState);
     QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
     QCOMPARE(player.media(), QMediaContent());
+    QCOMPARE(player.currentMedia(), QMediaContent());
 
     QVERIFY(!stateSpy.isEmpty());
     QVERIFY(!statusSpy.isEmpty());
     QVERIFY(!mediaSpy.isEmpty());
+    QVERIFY(!currentMediaSpy.isEmpty());
     QVERIFY(!positionSpy.isEmpty());
 }
 
@@ -327,7 +343,7 @@ void tst_QMediaPlayerBackend::playPauseStop()
     QTRY_VERIFY(statusSpy.count() > 0 &&
                 statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::BufferedMedia);
 
-    QTRY_VERIFY(player.position() > 0);
+    QTRY_VERIFY(player.position() > 100);
     QVERIFY(player.duration() > 0);
     QVERIFY(positionSpy.count() > 0);
     QVERIFY(positionSpy.last()[0].value<qint64>() > 0);
@@ -361,6 +377,63 @@ void tst_QMediaPlayerBackend::playPauseStop()
     QCOMPARE(player.position(), qint64(0));
     QCOMPARE(positionSpy.last()[0].value<qint64>(), qint64(0));
     QVERIFY(player.duration() > 0);
+
+    stateSpy.clear();
+    statusSpy.clear();
+    positionSpy.clear();
+
+    player.play();
+
+    QCOMPARE(player.state(), QMediaPlayer::PlayingState);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+    QCOMPARE(stateSpy.count(), 1);
+    QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState);
+    QCOMPARE(statusSpy.count(), 1); // Should not go through Loading again when play -> stop -> play
+    QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
+
+    player.stop();
+    stateSpy.clear();
+    statusSpy.clear();
+    positionSpy.clear();
+
+    player.setMedia(localWavFile2);
+
+    QTRY_VERIFY(statusSpy.count() > 0);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+    QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia);
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.count(), 0);
+
+    player.play();
+
+    QTRY_VERIFY(player.position() > 100);
+
+    player.setMedia(localWavFile);
+
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+    QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia);
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState);
+    QCOMPARE(player.position(), 0);
+    QCOMPARE(positionSpy.last()[0].value<qint64>(), 0);
+
+    stateSpy.clear();
+    statusSpy.clear();
+    positionSpy.clear();
+
+    player.play();
+
+    QTRY_VERIFY(player.position() > 100);
+
+    player.setMedia(QMediaContent());
+
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
+    QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::NoMedia);
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState);
+    QCOMPARE(player.position(), 0);
+    QCOMPARE(positionSpy.last()[0].value<qint64>(), 0);
+    QCOMPARE(player.duration(), 0);
 }
 
 
@@ -383,17 +456,25 @@ void tst_QMediaPlayerBackend::processEOS()
 
     QVERIFY(statusSpy.count() > 0);
     QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.count(), 2);
+    QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState);
 
     //at EOS the position stays at the end of file
-    QVERIFY(player.position() > 900);
+    QCOMPARE(player.position(), player.duration());
+    QVERIFY(positionSpy.count() > 0);
+    QCOMPARE(positionSpy.last()[0].value<qint64>(), player.duration());
 
     stateSpy.clear();
     statusSpy.clear();
+    positionSpy.clear();
 
     player.play();
 
     //position is reset to start
     QTRY_VERIFY(player.position() < 100);
+    QVERIFY(positionSpy.count() > 0);
+    QCOMPARE(positionSpy.first()[0].value<qint64>(), 0);
 
     QCOMPARE(player.state(), QMediaPlayer::PlayingState);
     QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
@@ -406,13 +487,16 @@ void tst_QMediaPlayerBackend::processEOS()
     player.setPosition(900);
     //wait up to 5 seconds for EOS
     QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+    QVERIFY(statusSpy.count() > 0);
+    QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.count(), 2);
+    QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState);
 
-    //ensure the positionChanged() signal is emitted
-    QVERIFY(positionSpy.count() > 0);
-
-    QCOMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
     //position stays at the end of file
-    QVERIFY(player.position() > 900);
+    QCOMPARE(player.position(), player.duration());
+    QVERIFY(positionSpy.count() > 0);
+    QCOMPARE(positionSpy.last()[0].value<qint64>(), player.duration());
 
     //after setPosition EndOfMedia status should be reset to Loaded
     stateSpy.clear();
@@ -608,7 +692,7 @@ void tst_QMediaPlayerBackend::initialVolume()
 void tst_QMediaPlayerBackend::seekPauseSeek()
 {
     if (localVideoFile.isNull())
-        QSKIP("Video format is not supported");
+        QSKIP("No supported video file");
 
     QMediaPlayer player;
 
@@ -674,6 +758,125 @@ void tst_QMediaPlayerBackend::seekPauseSeek()
     }
 }
 
+void tst_QMediaPlayerBackend::seekInStoppedState()
+{
+    if (localVideoFile.isNull())
+        QSKIP("No supported video file");
+
+    QMediaPlayer player;
+    player.setNotifyInterval(500);
+
+    QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
+    QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
+
+    player.setMedia(localVideoFile);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(player.position(), 0);
+    QVERIFY(player.isSeekable());
+
+    stateSpy.clear();
+    positionSpy.clear();
+
+    qint64 position = 5000;
+    player.setPosition(position);
+
+    QTRY_VERIFY(qAbs(player.position() - position) < qint64(500));
+    QCOMPARE(positionSpy.count(), 1);
+    QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500));
+
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.count(), 0);
+
+    QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+    positionSpy.clear();
+
+    player.play();
+
+    QCOMPARE(player.state(), QMediaPlayer::PlayingState);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+    QVERIFY(qAbs(player.position() - position) < qint64(500));
+
+    QTest::qWait(2000);
+    // Check that it never played from the beginning
+    QVERIFY(player.position() > (position - 500));
+    for (int i = 0; i < positionSpy.count(); ++i)
+        QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500));
+
+    // ------
+    // Same tests but after play() --> stop()
+
+    player.stop();
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+    QCOMPARE(player.position(), 0);
+
+    stateSpy.clear();
+    positionSpy.clear();
+
+    player.setPosition(position);
+
+    QTRY_VERIFY(qAbs(player.position() - position) < qint64(500));
+    QCOMPARE(positionSpy.count(), 1);
+    QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500));
+
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.count(), 0);
+
+    QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+    positionSpy.clear();
+
+    player.play();
+
+    QCOMPARE(player.state(), QMediaPlayer::PlayingState);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+    QVERIFY(qAbs(player.position() - position) < qint64(500));
+
+    QTest::qWait(2000);
+    // Check that it never played from the beginning
+    QVERIFY(player.position() > (position - 500));
+    for (int i = 0; i < positionSpy.count(); ++i)
+        QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500));
+
+    // ------
+    // Same tests but after reaching the end of the media
+
+    player.setPosition(player.duration() - 500);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(player.position(), player.duration());
+
+    stateSpy.clear();
+    positionSpy.clear();
+
+    player.setPosition(position);
+
+    QTRY_VERIFY(qAbs(player.position() - position) < qint64(500));
+    QCOMPARE(positionSpy.count(), 1);
+    QVERIFY(qAbs(positionSpy.last()[0].value<qint64>() - position) < qint64(500));
+
+    QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+    QCOMPARE(stateSpy.count(), 0);
+
+    QCOMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+    positionSpy.clear();
+
+    player.play();
+
+    QCOMPARE(player.state(), QMediaPlayer::PlayingState);
+    QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+    QVERIFY(qAbs(player.position() - position) < qint64(500));
+
+    QTest::qWait(2000);
+    // Check that it never played from the beginning
+    QVERIFY(player.position() > (position - 500));
+    for (int i = 0; i < positionSpy.count(); ++i)
+        QVERIFY(positionSpy.at(i)[0].value<qint64>() > (position - 500));
+}
+
 void tst_QMediaPlayerBackend::subsequentPlayback()
 {
 #ifdef Q_OS_LINUX
@@ -717,7 +920,7 @@ void tst_QMediaPlayerBackend::subsequentPlayback()
 void tst_QMediaPlayerBackend::probes()
 {
     if (localVideoFile.isNull())
-        QSKIP("Video format is not supported");
+        QSKIP("No supported video file");
 
     QMediaPlayer *player = new QMediaPlayer;
 
@@ -733,8 +936,9 @@ void tst_QMediaPlayerBackend::probes()
     connect(audioProbe, SIGNAL(audioBufferProbed(QAudioBuffer)), &probeHandler, SLOT(processBuffer(QAudioBuffer)));
     connect(audioProbe, SIGNAL(flush()), &probeHandler, SLOT(flushAudio()));
 
-    QVERIFY(videoProbe->setSource(player));
-    QVERIFY(audioProbe->setSource(player));
+    if (!videoProbe->setSource(player))
+        QSKIP("QVideoProbe is not supported");
+    audioProbe->setSource(player);
 
     player->setMedia(localVideoFile);
     QTRY_COMPARE(player->mediaStatus(), QMediaPlayer::LoadedMedia);
@@ -831,7 +1035,7 @@ void tst_QMediaPlayerBackend::playlist()
     errorSpy.clear();
 
     // <<< Invalid2 - 1st pass >>>
-    fileInfo.setFile((QFINDTESTDATA("testdata/invalid_media2.m3u")));
+    fileInfo.setFile(QFINDTESTDATA("/testdata/invalid_media2.m3u"));
     player.setMedia(QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
 
     player.play();
@@ -864,7 +1068,7 @@ void tst_QMediaPlayerBackend::playlist()
     errorSpy.clear();
 
     // <<< Recursive - 1st pass >>>
-    fileInfo.setFile((QFINDTESTDATA("testdata/recursive_master.m3u")));
+    fileInfo.setFile(QFINDTESTDATA("testdata/recursive_master.m3u"));
     player.setMedia(QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
 
     player.play();
@@ -929,7 +1133,7 @@ void tst_QMediaPlayerBackend::surfaceTest()
 {
     // 25 fps video file
     if (localVideoFile.isNull())
-        QSKIP("Video format is not supported");
+        QSKIP("No supported video file");
 
     QFETCH(QList<QVideoFrame::PixelFormat>, formatsList);