diff options
author | Artem Dyomin <artem.dyomin@qt.io> | 2023-11-08 13:34:47 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-11-08 20:01:07 +0000 |
commit | 9c401460f807923f429f9b4d118f24416fa85f43 (patch) | |
tree | 1b659f751c65a6ced4ea1b33f392b67a6847f122 | |
parent | 7dc43a2d8f15b4d5e945198f0adb01cbbb2cce0a (diff) |
Fix waiting for the end frame in the ffmpeg mediaplayer
The problem was in immediate handling of the ending frame after
the last frame; we should wait for the last frame ending before
finalization the rendering.
Pick-to: 6.5
Change-Id: Ie5e30419678ac3a4d5c0d722d99beb8c197bcb46
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
Reviewed-by: Jøger Hansegård <joger.hansegard@qt.io>
(cherry picked from commit 4b8077ed171bce50f5ba97caec2058745cbfc44f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
4 files changed, 54 insertions, 9 deletions
diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp index f3ab4699e..f5381a41c 100644 --- a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp +++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer.cpp @@ -12,7 +12,8 @@ static Q_LOGGING_CATEGORY(qLcRenderer, "qt.multimedia.ffmpeg.renderer"); Renderer::Renderer(const TimeController &tc, const std::chrono::microseconds &seekPosTimeOffset) : m_timeController(tc), - m_lastPosition(tc.currentPosition()), + m_lastFrameEnd(tc.currentPosition()), + m_lastPosition(m_lastFrameEnd), m_seekPos(tc.currentPosition(-seekPosTimeOffset)) { } @@ -119,17 +120,24 @@ float Renderer::playbackRate() const int Renderer::timerInterval() const { - auto frame = !m_frames.empty() ? m_frames.front() : Frame(); - if (frame.isValid()) { - using namespace std::chrono; + if (m_frames.empty()) + return 0; - const auto nextTime = m_explicitNextFrameTime - ? *m_explicitNextFrameTime - : m_timeController.timeFromPosition(frame.absolutePts()); + auto calculateInterval = [](const TimePoint &nextTime) { + using namespace std::chrono; const auto delay = nextTime - Clock::now(); return std::max(0, static_cast<int>(duration_cast<milliseconds>(delay).count())); - } + }; + + if (m_explicitNextFrameTime) + return calculateInterval(*m_explicitNextFrameTime); + + if (m_frames.front().isValid()) + return calculateInterval(m_timeController.timeFromPosition(m_frames.front().absolutePts())); + + if (m_lastFrameEnd > 0) + return calculateInterval(m_timeController.timeFromPosition(m_lastFrameEnd)); return 0; } @@ -163,7 +171,10 @@ void Renderer::doNextStep() if (frame.isValid()) { m_lastPosition.storeRelease(std::max(frame.absolutePts(), lastPosition())); - m_seekPos.storeRelaxed(frame.absoluteEnd()); + + // TODO: get rid of m_lastFrameEnd or m_seekPos + m_lastFrameEnd = frame.absoluteEnd(); + m_seekPos.storeRelaxed(m_lastFrameEnd); const auto loopIndex = frame.loopOffset().index; if (m_loopIndex < loopIndex) { @@ -172,6 +183,8 @@ void Renderer::doNextStep() } emit frameProcessed(frame); + } else { + m_lastPosition.storeRelease(std::max(m_lastFrameEnd, lastPosition())); } } else { m_explicitNextFrameTime = Clock::now() + result.recheckInterval; diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h index 863ca60af..e8ee44d84 100644 --- a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h +++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegrenderer_p.h @@ -104,8 +104,10 @@ private: private: TimeController m_timeController; + qint64 m_lastFrameEnd = 0; QAtomicInteger<qint64> m_lastPosition = 0; QAtomicInteger<qint64> m_seekPos = 0; + int m_loopIndex = 0; QQueue<Frame> m_frames; diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/one_red_frame.mp4 b/tests/auto/integration/qmediaplayerbackend/testdata/one_red_frame.mp4 Binary files differnew file mode 100644 index 000000000..db8e44a3f --- /dev/null +++ b/tests/auto/integration/qmediaplayerbackend/testdata/one_red_frame.mp4 diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp index fec78f5a1..892efbe16 100644 --- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp +++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp @@ -110,6 +110,7 @@ private slots: void play_doesNotEnterMediaLoadingState_whenResumingPlayingAfterStop(); void playAndSetSource_emitsExpectedSignalsAndStopsPlayback_whenSetSourceWasCalledWithEmptyUrl(); void play_createsFramesWithExpectedContentAndIncreasingFrameTime_whenPlayingRtspMediaStream(); + void play_waitsForLastFrameEnd_whenPlayingVideoWithLongFrames(); void stop_entersStoppedState_whenPlayerWasPaused(); @@ -176,6 +177,7 @@ private: QUrl m_localCompressedSoundFile; QUrl m_localFileWithMetadata; QUrl m_localVideoFile3ColorsWithSound; + QUrl m_oneRedFrameVideo; const std::array<QRgb, 3> m_video3Colors = { { 0xFF0000, 0x00FF00, 0x0000FF } }; QString m_vlcCommand; @@ -287,8 +289,12 @@ void tst_QMediaPlayerBackend::initTestCase() mediaCandidates << "qrc:/testdata/nokia-tune.mp3"; mediaCandidates << "qrc:/testdata/nokia-tune.mkv"; m_localCompressedSoundFile = MediaFileSelector::selectMediaFile(mediaCandidates); + m_localFileWithMetadata = MediaFileSelector::selectMediaFile(QStringList() << "qrc:/testdata/nokia-tune.mp3"); + m_oneRedFrameVideo = + MediaFileSelector::selectMediaFile(QStringList() << "qrc:/testdata/one_red_frame.mp4"); + detectVlcCommand(); } @@ -947,6 +953,30 @@ void tst_QMediaPlayerBackend:: QCOMPARE(errorSpy.size(), 0); } +void tst_QMediaPlayerBackend::play_waitsForLastFrameEnd_whenPlayingVideoWithLongFrames() +{ + m_fixture->player.setSource(m_oneRedFrameVideo); + m_fixture->player.play(); + + auto firstFrame = m_fixture->surface.waitForFrame(); + QVERIFY(firstFrame.isValid()); + + QElapsedTimer timer; + timer.start(); + + auto endFrame = m_fixture->surface.waitForFrame(); + QVERIFY(!endFrame.isValid()); + + const auto elapsed = timer.elapsed(); + + // 1000 is expected + QCOMPARE_GT(elapsed, 900); + QCOMPARE_LT(elapsed, 1400); + + QTRY_COMPARE(m_fixture->player.mediaStatus(), QMediaPlayer::EndOfMedia); + QCOMPARE(m_fixture->surface.m_totalFrames, 2); +} + void tst_QMediaPlayerBackend::stop_entersStoppedState_whenPlayerWasPaused() { if (!isWavSupported()) |