summaryrefslogtreecommitdiff
path: root/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp')
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp105
1 files changed, 85 insertions, 20 deletions
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
index 19105499d..c9c4f20f2 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegplaybackengine.cpp
@@ -18,6 +18,8 @@ QT_BEGIN_NAMESPACE
namespace QFFmpeg {
+static Q_LOGGING_CATEGORY(qLcPlaybackEngine, "qt.multimedia.ffmpeg.playbackengine");
+
// The helper is needed since on some compilers std::unique_ptr
// doesn't have a default constructor in the case of sizeof(CustomDeleter) > 0
template<typename Array>
@@ -41,11 +43,13 @@ PlaybackEngine::PlaybackEngine()
m_streams(defaultObjectsArray<decltype(m_streams)>()),
m_renderers(defaultObjectsArray<decltype(m_renderers)>())
{
+ qCDebug(qLcPlaybackEngine) << "Create PlaybackEngine";
qRegisterMetaType<QFFmpeg::Packet>();
qRegisterMetaType<QFFmpeg::Frame>();
}
PlaybackEngine::~PlaybackEngine() {
+ qCDebug(qLcPlaybackEngine) << "Delete PlaybackEngine";
forEachExistingObject([](auto &object) { object.reset(); });
deleteFreeThreads();
}
@@ -62,22 +66,33 @@ void PlaybackEngine::onRendererFinished()
if (!isAtEnd(QPlatformMediaPlayer::AudioStream))
return;
- if (!isAtEnd(QPlatformMediaPlayer::SubtitleStream)
- && !m_renderers[QPlatformMediaPlayer::AudioStream]
- && !m_renderers[QPlatformMediaPlayer::VideoStream])
+ if (!isAtEnd(QPlatformMediaPlayer::SubtitleStream) && !hasMediaStream())
return;
if (std::exchange(m_state, QMediaPlayer::StoppedState) == QMediaPlayer::StoppedState)
return;
- m_timeController.setPaused(true);
- m_timeController.sync(m_duration);
+ finilizeTime(duration());
forceUpdate();
+ qCDebug(qLcPlaybackEngine) << "Playback engine end of stream";
+
emit endOfStream();
}
+void PlaybackEngine::onRendererLoopChanged(qint64 offset, int loopIndex)
+{
+ if (loopIndex > m_currentLoopOffset.index) {
+ m_currentLoopOffset = { offset, loopIndex };
+ emit loopChanged();
+ } else if (loopIndex == m_currentLoopOffset.index && offset != m_currentLoopOffset.pos) {
+ qWarning() << "Unexpected offset for loop" << loopIndex << ":" << offset << "vs"
+ << m_currentLoopOffset.pos;
+ m_currentLoopOffset.pos = offset;
+ }
+}
+
void PlaybackEngine::onRendererSynchronized(std::chrono::steady_clock::time_point tp, qint64 pos)
{
Q_ASSERT(QObject::sender() == m_renderers[QPlatformMediaPlayer::AudioStream].get());
@@ -94,15 +109,13 @@ void PlaybackEngine::setState(QMediaPlayer::PlaybackState state) {
if (!m_context)
return;
- if ( state == m_state )
+ if (state == m_state)
return;
const auto prevState = std::exchange(m_state, state);
- if (m_state == QMediaPlayer::StoppedState) {
- m_timeController.setPaused(true);
- m_timeController.sync();
- }
+ if (m_state == QMediaPlayer::StoppedState)
+ finilizeTime(0);
if (prevState == QMediaPlayer::StoppedState || m_state == QMediaPlayer::StoppedState)
recreateObjects();
@@ -208,14 +221,31 @@ void PlaybackEngine::forEachExistingObject(Action &&action)
void PlaybackEngine::seek(qint64 pos)
{
- pos = qBound(0, pos, m_duration);
+ pos = qBound(0, pos, duration());
m_timeController.setPaused(true);
- m_timeController.sync(pos);
+ m_timeController.sync(m_currentLoopOffset.pos + pos);
forceUpdate();
}
+void PlaybackEngine::setLoops(int loops)
+{
+ if (!isSeekable()) {
+ qWarning() << "Cannot set loops for non-seekable source";
+ return;
+ }
+
+ if (std::exchange(m_loops, loops) == loops)
+ return;
+
+ qCDebug(qLcPlaybackEngine) << "set playback engine loops:" << loops << "prev loops:" << m_loops
+ << "index:" << m_currentLoopOffset.index;
+
+ if (m_demuxer)
+ m_demuxer->setLoops(loops);
+}
+
void PlaybackEngine::triggerStepIfNeeded()
{
if (m_state != QMediaPlayer::PausedState)
@@ -295,6 +325,9 @@ void PlaybackEngine::createStreamAndRenderer(QPlatformMediaPlayer::TrackType tra
connect(renderer.get(), &Renderer::synchronized, this,
&PlaybackEngine::onRendererSynchronized);
+ connect(renderer.get(), &Renderer::loopChanged, this,
+ &PlaybackEngine::onRendererLoopChanged);
+
if constexpr (shouldPauseStreams)
connect(renderer.get(), &Renderer::forceStepDone, this,
&PlaybackEngine::updateObjectsPausedState);
@@ -337,6 +370,8 @@ std::optional<Codec> PlaybackEngine::codecForTrack(QPlatformMediaPlayer::TrackTy
auto &result = m_codecs[trackType];
if (!result) {
+ qCDebug(qLcPlaybackEngine)
+ << "Create codec for stream:" << streamIndex << "trackType:" << trackType;
auto maybeCodec = Codec::create(m_context->streams[streamIndex]);
if (!maybeCodec) {
@@ -351,6 +386,12 @@ std::optional<Codec> PlaybackEngine::codecForTrack(QPlatformMediaPlayer::TrackTy
return result;
}
+bool PlaybackEngine::hasMediaStream() const
+{
+ return m_renderers[QPlatformMediaPlayer::AudioStream]
+ || m_renderers[QPlatformMediaPlayer::VideoStream];
+}
+
void PlaybackEngine::createDemuxer()
{
decltype(m_currentAVStreamIndex) streamIndexes = { -1, -1, -1 };
@@ -365,8 +406,10 @@ void PlaybackEngine::createDemuxer()
if (!hasStreams)
return;
- m_demuxer =
- createPlaybackEngineObject<Demuxer>(m_context.get(), currentPosition(), streamIndexes);
+ const PositionWithOffset positionWithOffset{ currentPosition(false), m_currentLoopOffset };
+
+ m_demuxer = createPlaybackEngineObject<Demuxer>(m_context.get(), positionWithOffset,
+ streamIndexes, m_loops);
forEachExistingObject<StreamDecoder>([&](auto &stream) {
connect(m_demuxer.get(), Demuxer::signalByTrackType(stream->trackType()), stream.get(),
@@ -425,14 +468,28 @@ void PlaybackEngine::setAudioSink(QAudioOutput *output)
forceUpdate();
}
-qint64 PlaybackEngine::currentPosition() const {
- auto pos = std::numeric_limits<qint64>::max();
+qint64 PlaybackEngine::currentPosition(bool topPos) const {
+ std::optional<qint64> pos;
+
+ for (size_t i = 0; i < m_renderers.size(); ++i) {
+ const auto &renderer = m_renderers[i];
+ if (!renderer)
+ continue;
+
+ // skip subtitle stream for finding lower rendering position
+ if (!topPos && i == QPlatformMediaPlayer::SubtitleStream && hasMediaStream())
+ continue;
+
+ const auto rendererPos = renderer->lastPosition();
+ pos = !pos ? rendererPos
+ : topPos ? std::max(*pos, rendererPos)
+ : std::min(*pos, rendererPos);
+ }
- for (auto trackType : { QPlatformMediaPlayer::VideoStream, QPlatformMediaPlayer::AudioStream })
- if (auto &renderer = m_renderers[trackType])
- pos = std::min(pos, renderer->lastPosition());
+ if (!pos)
+ pos = m_timeController.currentPosition();
- return pos == std::numeric_limits<qint64>::max() ? m_timeController.currentPosition() : pos;
+ return qBound(0, *pos - m_currentLoopOffset.pos, duration());
}
void PlaybackEngine::setActiveTrack(QPlatformMediaPlayer::TrackType trackType, int streamNumber)
@@ -450,6 +507,14 @@ void PlaybackEngine::setActiveTrack(QPlatformMediaPlayer::TrackType trackType, i
updateObjectsPausedState();
}
+void PlaybackEngine::finilizeTime(qint64 pos)
+{
+ Q_ASSERT(pos >= 0 && pos <= duration());
+
+ m_timeController.setPaused(true);
+ m_timeController.sync(pos);
+ m_currentLoopOffset = {};
+}
}
QT_END_NAMESPACE