7#include "private/qplatformaudiooutput_p.h"
25template<
typename Array>
28 using T =
typename Array::value_type;
29 return { T{ {}, {} }, T{ {}, {} }, T{ {}, {} } };
46 qCDebug(qLcPlaybackEngine) <<
"Create PlaybackEngine";
47 qRegisterMetaType<QFFmpeg::Packet>();
48 qRegisterMetaType<QFFmpeg::Frame>();
52 qCDebug(qLcPlaybackEngine) <<
"Delete PlaybackEngine";
55 forEachExistingObject([](
auto &
object) {
object.reset(); });
59void PlaybackEngine::onRendererFinished()
61 auto isAtEnd = [
this](
auto trackType) {
62 return !m_renderers[trackType] || m_renderers[trackType]->isAtEnd();
81 qCDebug(qLcPlaybackEngine) <<
"Playback engine end of stream";
91 if (loopIndex > m_currentLoopOffset.
index) {
92 m_currentLoopOffset = {
offset, loopIndex };
94 }
else if (loopIndex == m_currentLoopOffset.
index &&
offset != m_currentLoopOffset.
pos) {
95 qWarning() <<
"Unexpected offset for loop" << loopIndex <<
":" <<
offset <<
"vs"
96 << m_currentLoopOffset.
pos;
101void PlaybackEngine::onRendererSynchronized(
quint64 id, std::chrono::steady_clock::time_point tp,
104 if (!hasRenderer(
id))
115 m_timeController.
sync(tp,
pos);
117 forEachExistingObject<Renderer>([&](
auto &
renderer) {
127 if (
state == m_state)
130 const auto prevState = std::exchange(m_state,
state);
141 triggerStepIfNeeded();
143 updateObjectsPausedState();
146void PlaybackEngine::updateObjectsPausedState()
151 forEachExistingObject([&](
auto &
object) {
152 bool objectPaused =
false;
154 if constexpr (std::is_same_v<
decltype(*object),
Renderer &>)
155 objectPaused = paused;
157 auto streamPaused = [](
bool p,
auto &
r) {
158 const auto needMoreFrames =
r &&
r->stepInProgress();
159 return p && !needMoreFrames;
162 if constexpr (std::is_same_v<
decltype(*object), StreamDecoder &>)
163 objectPaused = streamPaused(paused,
renderer(
object->trackType()));
165 objectPaused = std::accumulate(m_renderers.begin(), m_renderers.end(), paused,
169 object->setPaused(objectPaused);
176 if (!std::exchange(
engine->m_threadsDirty,
true))
186 auto threadName = objectThreadName(
object);
187 auto &
thread = m_threads[threadName];
189 thread = std::make_unique<QThread>();
195 object.moveToThread(
thread.get());
204 ? createPlaybackEngineObject<VideoRenderer>(m_timeController, m_videoSink)
208 ? createPlaybackEngineObject<AudioRenderer>(m_timeController, m_audioOutput)
212 ? createPlaybackEngineObject<SubtitleRenderer>(m_timeController, m_videoSink)
219template<
typename C,
typename Action>
220void PlaybackEngine::forEachExistingObject(Action &&action)
222 auto handleNotNullObject = [&](
auto &
object) {
223 if constexpr (std::is_base_of_v<C, std::remove_reference_t<
decltype(*object)>>)
228 handleNotNullObject(m_demuxer);
229 std::for_each(m_streams.begin(), m_streams.end(), handleNotNullObject);
230 std::for_each(m_renderers.begin(), m_renderers.end(), handleNotNullObject);
233template<
typename Action>
234void PlaybackEngine::forEachExistingObject(Action &&action)
236 forEachExistingObject<PlaybackEngineObject>(std::forward<Action>(action));
244 m_timeController.
sync(m_currentLoopOffset.
pos +
pos);
252 qWarning() <<
"Cannot set loops for non-seekable source";
256 if (std::exchange(m_loops, loops) == loops)
259 qCDebug(qLcPlaybackEngine) <<
"set playback engine loops:" << loops <<
"prev loops:" << m_loops
260 <<
"index:" << m_currentLoopOffset.
index;
263 m_demuxer->setLoops(loops);
266void PlaybackEngine::triggerStepIfNeeded()
279QString PlaybackEngine::objectThreadName(
const PlaybackEngineObject &
object)
282 if (
auto stream = qobject_cast<const StreamDecoder *>(&
object))
300void PlaybackEngine::recreateObjects()
304 forEachExistingObject([](
auto &
object) {
object.reset(); });
306 createObjectsIfNeeded();
309void PlaybackEngine::createObjectsIfNeeded()
320void PlaybackEngine::forceUpdate()
323 triggerStepIfNeeded();
324 updateObjectsPausedState();
329 auto codec = codecForTrack(trackType);
331 auto &
renderer = m_renderers[trackType];
343 &PlaybackEngine::onRendererSynchronized);
346 &PlaybackEngine::onRendererLoopChanged);
350 &PlaybackEngine::updateObjectsPausedState);
353 &PlaybackEngine::onRendererFinished);
356 auto &
stream = m_streams[trackType] =
357 createPlaybackEngineObject<StreamDecoder>(*
codec,
renderer->seekPosition());
374 auto &
result = m_codecs[trackType];
378 <<
"Create codec for stream:" << streamIndex <<
"trackType:" << trackType;
383 "Cannot create codec," + maybeCodec.error());
387 result = maybeCodec.value();
393bool PlaybackEngine::hasMediaStream()
const
399void PlaybackEngine::createDemuxer()
403 bool hasStreams =
false;
404 forEachExistingObject<StreamDecoder>([&](
auto &
stream) {
406 const auto trackType =
stream->trackType();
413 const PositionWithOffset positionWithOffset{
currentPosition(
false), m_currentLoopOffset };
415 m_demuxer = createPlaybackEngineObject<Demuxer>(
m_context.get(), positionWithOffset,
416 streamIndexes, m_loops);
418 forEachExistingObject<StreamDecoder>([&](
auto &
stream) {
428void PlaybackEngine::deleteFreeThreads() {
429 m_threadsDirty =
false;
430 auto freeThreads = std::move(m_threads);
432 forEachExistingObject([&](
auto &
object) {
433 m_threads.insert(freeThreads.extract(objectThreadName(*
object)));
436 for (
auto &[
name, thr] : freeThreads)
439 for (
auto &[
name, thr] : freeThreads)
459 auto prev = std::exchange(m_videoSink,
sink);
465 if (!
sink || !prev) {
477 auto prev = std::exchange(m_audioOutput,
output);
490 std::optional<qint64>
pos;
492 for (
size_t i = 0;
i < m_renderers.size(); ++
i) {
501 const auto rendererPos =
renderer->lastPosition();
503 : topPos ? std::max(*
pos, rendererPos)
504 : std::min(*
pos, rendererPos);
518 m_codecs[trackType] = {};
520 m_renderers[trackType].reset();
521 m_streams = defaultObjectsArray<decltype(m_streams)>();
524 createObjectsIfNeeded();
525 updateObjectsPausedState();
528void PlaybackEngine::finilizeTime(
qint64 pos)
534 m_currentLoopOffset = {};
537void PlaybackEngine::finalizeOutputs()
543bool PlaybackEngine::hasRenderer(
quint64 id)
const
545 return std::any_of(m_renderers.begin(), m_renderers.end(),
546 [
id](
auto &
renderer) { return renderer && renderer->id() == id; });
558 if (
auto renderer = qobject_cast<SubtitleRenderer *>(
569#include "moc_qffmpegplaybackengine_p.cpp"
\qmltype AudioOutput \instantiates QAudioOutput
static QMaybe< Codec > create(AVStream *)
void onPacketProcessed(Packet)
static RequestingSignal signalByTrackType(QPlatformMediaPlayer::TrackType trackType)
void error(int code, const QString &errorString)
void setLoops(int loopsCount)
void setVideoSink(QVideoSink *sink)
void setState(QMediaPlayer::PlaybackState state)
virtual RendererPtr createRenderer(QPlatformMediaPlayer::TrackType trackType)
void setPlaybackRate(float rate)
void setAudioSink(QAudioOutput *output)
qint64 currentPosition(bool topPos=true) const
void errorOccured(int, const QString &)
ObjectPtr< Renderer > RendererPtr
bool setMedia(const QUrl &media, QIODevice *stream)
~PlaybackEngine() override
void setActiveTrack(QPlatformMediaPlayer::TrackType type, int streamNumber)
void updateActiveAudioOutput(QAudioOutput *output)
void updateActiveVideoOutput(QVideoSink *sink, bool cleanOutput=false)
float playbackRate() const
void synchronized(Id id, TimePoint tp, qint64 pos)
void frameProcessed(Frame)
void onFinalFrameReceived()
void loopChanged(Id id, qint64 offset, int index)
void onFrameProcessed(Frame frame)
void packetProcessed(Packet)
void onFinalPacketReceived()
void requestHandleFrame(Frame frame)
void sync(qint64 trackPos=0)
qint64 positionFromTime(TimePoint tp, bool ignorePause=false) const
void setPlaybackRate(PlaybackRate playbackRate)
qint64 currentPosition(const Clock::duration &offset=Clock::duration{ 0 }) const
void setPaused(bool paused)
float playbackRate() const
\inmodule QtCore \reentrant
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
QThread * thread() const
Returns the thread in which the object lives.
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void start(Priority=InheritPriority)
The QVideoSink class represents a generic sink for video data.
Array defaultObjectsArray()
static constexpr bool shouldPauseStreams
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
static QDBusError::ErrorType get(const char *name)
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
constexpr const T & qBound(const T &min, const T &val, const T &max)
GLenum GLuint GLintptr offset
GLsizei GLenum GLboolean sink
unsigned long long quint64
QT_BEGIN_NAMESPACE typedef uchar * output
QSvgRenderer * renderer
[0]
void operator()(PlaybackEngineObject *) const