diff --git a/ffmpeg/CMakeLists.txt b/ffmpeg/CMakeLists.txt index 49ea8dc..1d4af4e 100644 --- a/ffmpeg/CMakeLists.txt +++ b/ffmpeg/CMakeLists.txt @@ -37,6 +37,8 @@ set(PROJECT_SOURCES averror.h averrormanager.cc averrormanager.hpp + clock.cc + clock.hpp codeccontext.cpp codeccontext.h colorspace.hpp diff --git a/ffmpeg/clock.cc b/ffmpeg/clock.cc new file mode 100644 index 0000000..df2dae1 --- /dev/null +++ b/ffmpeg/clock.cc @@ -0,0 +1,139 @@ +#include "clock.hpp" + +#include + +extern "C" { +#include +} + +namespace Ffmpeg { + +class Clock::ClockPrivate +{ +public: + explicit ClockPrivate(Clock *q) + : q_ptr(q) + {} + + Clock *q_ptr; + + mutable QMutex mutex; + qint64 pts = 0; // 当前 AVFrame 的时间戳 microseconds + qint64 pts_drift = 0; // 时钟漂移量,用于计算当前时钟的状态 microseconds + qint64 last_updated = av_gettime_relative(); // 上一次更新时钟状态的时间 microseconds + bool paused = false; // 是否暂停播放 + + static constexpr QPair speedRange = {0.5, 3.0}; + static constexpr auto speedStep = 0.5; + static std::atomic speed; + + static constexpr auto diffThreshold = 50 * 1000; // 50ms + + static Clock *g_clock; +}; + +std::atomic Clock::ClockPrivate::speed = 1.0; +Clock *Clock::ClockPrivate::g_clock = nullptr; + +Clock::Clock(QObject *parent) + : QObject{parent} + , d_ptr(new ClockPrivate(this)) +{} + +Clock::~Clock() = default; + +auto Clock::pts() -> qint64 +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->pts; +} + +auto Clock::ptsDrift() -> qint64 +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->pts_drift; +} + +auto Clock::lastUpdated() -> qint64 +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->last_updated; +} + +auto Clock::paused() -> bool +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->paused; +} + +void Clock::setPaused(bool value) +{ + QMutexLocker locker(&d_ptr->mutex); + d_ptr->paused = value; + if (!d_ptr->paused) { + d_ptr->last_updated = av_gettime_relative(); + } +} + +void Clock::update(qint64 pts, qint64 time) +{ + QMutexLocker locker(&d_ptr->mutex); + if (d_ptr->last_updated && !d_ptr->paused) { + qint64 time_diff = (time - d_ptr->last_updated) * speed(); + d_ptr->pts_drift = (pts - d_ptr->pts) - time_diff; + } + d_ptr->pts = pts; + d_ptr->last_updated = time; +} + +auto Clock::getDelayWithMaster() -> qint64 +{ + Q_ASSERT(Clock::ClockPrivate::g_clock); + auto pts_drift = ptsDrift(); + if (this == Clock::ClockPrivate::g_clock) { + return pts_drift; + } + pts_drift -= Clock::ClockPrivate::g_clock->ptsDrift(); + return pts_drift; +} + +auto Clock::adjustDelay(qint64 &delay) -> bool +{ + if (speed() > 1.0 && delay < 0) { + return false; + } else if (delay < -Clock::ClockPrivate::diffThreshold) { + return false; + } else if (qAbs(delay) <= Clock::ClockPrivate::diffThreshold) { + delay = 0; + return true; + } + delay -= Clock::ClockPrivate::diffThreshold; + return true; +} + +auto Clock::speedRange() -> QPair +{ + return Clock::ClockPrivate::speedRange; +} + +auto Clock::speedStep() -> double +{ + return Clock::ClockPrivate::speedStep; +} + +void Clock::setSpeed(double value) +{ + Clock::ClockPrivate::speed.store(value); +} + +auto Clock::speed() -> double +{ + return Clock::ClockPrivate::speed.load(); +} + +void Clock::setMaster(Clock *clock) +{ + Clock::ClockPrivate::g_clock = clock; +} + +} // namespace Ffmpeg diff --git a/ffmpeg/clock.hpp b/ffmpeg/clock.hpp new file mode 100644 index 0000000..f65209d --- /dev/null +++ b/ffmpeg/clock.hpp @@ -0,0 +1,43 @@ +#ifndef CLOCK_HPP +#define CLOCK_HPP + +#include + +namespace Ffmpeg { + +class Clock : public QObject +{ +public: + explicit Clock(QObject *parent = nullptr); + ~Clock() override; + + auto pts() -> qint64; + auto ptsDrift() -> qint64; + auto lastUpdated() -> qint64; + + void setPaused(bool value); + auto paused() -> bool; + + void update(qint64 pts, qint64 time); + + auto getDelayWithMaster() -> qint64; + + // return true if delay is valid + static auto adjustDelay(qint64 &delay) -> bool; + + auto speedRange() -> QPair; + auto speedStep() -> double; + static void setSpeed(double value); + static auto speed() -> double; + + // not thread safe and not delete clock + static void setMaster(Clock *clock); + +private: + class ClockPrivate; + QScopedPointer d_ptr; +}; + +} // namespace Ffmpeg + +#endif // CLOCK_HPP diff --git a/ffmpeg/ffmpeg.pro b/ffmpeg/ffmpeg.pro index acc70bd..aecd18f 100644 --- a/ffmpeg/ffmpeg.pro +++ b/ffmpeg/ffmpeg.pro @@ -19,6 +19,7 @@ SOURCES += \ avcontextinfo.cpp \ averror.cpp \ averrormanager.cc \ + clock.cc \ codeccontext.cpp \ decoder.cc \ decoderaudioframe.cpp \ @@ -44,6 +45,7 @@ HEADERS += \ avcontextinfo.h \ averror.h \ averrormanager.hpp \ + clock.hpp \ codeccontext.h \ colorspace.hpp \ decoder.h \