Revision control

Copy as Markdown

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFFMPEGPLAYBACKENGINEOBJECT_P_H
#define QFFMPEGPLAYBACKENGINEOBJECT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qatomic.h>
#include <QtCore/qthread.h>
#include <QtCore/qcoreevent.h>
#include <QtCore/qcoreapplication.h>
#include <QtMultimedia/qmediaplayer.h>
#include <QtFFmpegMediaPluginImpl/private/qffmpegplaybackenginedefs_p.h>
#include <QtFFmpegMediaPluginImpl/private/qffmpegplaybackutils_p.h>
#include <chrono>
#include <optional>
QT_BEGIN_NAMESPACE
class QChronoTimer;
namespace QFFmpeg {
class PlaybackEngineObject : public QObject
{
Q_OBJECT
static constexpr QEvent::Type FuncEventType = QEvent::User;
class FuncEvent : public QEvent
{
public:
FuncEvent() : QEvent(FuncEventType) { }
virtual void invoke() = 0;
};
template <typename F>
class FuncEventImpl final : public FuncEvent
{
public:
explicit FuncEventImpl(F &&f) : m_func(std::forward<F>(f)) { }
void invoke() override { m_func(); }
private:
std::decay_t<F> m_func;
};
public:
using TimePoint = std::chrono::steady_clock::time_point;
using TimePointOpt = std::optional<TimePoint>;
explicit PlaybackEngineObject(const PlaybackEngineObjectID &id);
~PlaybackEngineObject() override;
bool isPaused() const;
bool isAtEnd() const;
void kill();
void setPaused(bool isPaused);
quint64 objectID() const { return m_id.objectID; }
Q_SIGNALS:
void atEnd(PlaybackEngineObjectID id);
void error(QMediaPlayer::Error, const QString &errorString);
protected:
bool event(QEvent *e) override;
bool checkSessionID(quint64 sessionID) const { return sessionID == m_id.sessionID; }
bool checkID(const PlaybackEngineObjectID &id) const
{
return checkSessionID(id.sessionID) && id.objectID == objectID();
}
const PlaybackEngineObjectID &id() const
{
Q_ASSERT(thread()->isCurrentThread());
return m_id;
}
template <typename F>
void updateSession(quint64 newID, F &&f)
{
m_invalidateCounter.fetch_add(1, std::memory_order_relaxed);
invokePriorityMethod([f = std::forward<F>(f), this, newID]() {
const int count = m_invalidateCounter.fetch_sub(1, std::memory_order_relaxed) - 1;
Q_ASSERT(count >= 0);
if (count)
return;
m_id.sessionID = newID;
setAtEnd(false);
f();
});
}
template <typename F>
void invokePriorityMethod(F &&f)
{
Q_ASSERT(!thread()->isCurrentThread());
// Note, that the event loop takes ownership of the event
QCoreApplication::postEvent(this, new FuncEventImpl<F>(std::forward<F>(f)),
Qt::HighEventPriority);
}
QChronoTimer &timer();
void scheduleNextStep();
virtual void onPauseChanged();
virtual bool canDoNextStep() const;
virtual TimePoint nextTimePoint() const;
void setAtEnd(bool isAtEnd);
virtual void doNextStep() { }
private Q_SLOTS:
void onTimeout();
private:
enum class StepType : uint8_t { None, Timeout, Immediate };
void doNextStep(StepType type);
bool isValid() const { return m_invalidateCounter.load(std::memory_order_relaxed) == 0; }
std::unique_ptr<QChronoTimer> m_timer;
QAtomicInteger<bool> m_paused = true;
QAtomicInteger<bool> m_atEnd = false;
std::atomic_int m_invalidateCounter = 0;
PlaybackEngineObjectID m_id;
TimePointOpt m_nextTimePoint;
TimePointOpt m_timePoint;
StepType m_stepType = StepType::None;
};
} // namespace QFFmpeg
QT_END_NAMESPACE
#endif // QFFMPEGPLAYBACKENGINEOBJECT_P_H