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..587caa8 --- /dev/null +++ b/ffmpeg/clock.cc @@ -0,0 +1,143 @@ +#include "clock.hpp" + +#include + +extern "C" { +#include +} + +namespace Ffmpeg { + +static Clock *g_clock = nullptr; + +class Clock::ClockPrivate +{ +public: + explicit ClockPrivate(Clock *q) + : q_ptr(q) + {} + + Clock *q_ptr; + + mutable QMutex mutex; + qint64 pts = 0; // 当前 AVFrame 的时间戳 + qint64 pts_drift = 0; // 时钟漂移量,用于计算当前时钟的状态 + qint64 last_updated = 0; // 上一次更新时钟状态的时间 + double speed = 1.0; // 播放速度,用于调整音视频的播放速度 + bool paused = false; // 是否暂停播放 + + static constexpr auto drop_threshold = 50 * 1000; // 50ms 丢帧阈值 + static constexpr auto sync_threshold = 10 * 1000; // 10ms 同步阈值 +}; + +Clock::Clock(QObject *parent) + : QObject{parent} + , d_ptr(new ClockPrivate(this)) +{} + +Clock::~Clock() = default; + +auto Clock::getPts() -> qint64 +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->pts; +} + +auto Clock::getPtsDrift() -> qint64 +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->pts_drift; +} + +auto Clock::getLastUpdated() -> qint64 +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->last_updated; +} + +auto Clock::getSpeed() -> double +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->speed; +} + +void Clock::setSpeed(double value) +{ + QMutexLocker locker(&d_ptr->mutex); + d_ptr->speed = value; +} + +auto Clock::isPaused() -> bool +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->paused; +} + +void Clock::setPaused(bool value) +{ + QMutexLocker locker(&d_ptr->mutex); + if (d_ptr->paused != value) { + d_ptr->paused = value; + if (d_ptr->paused) { + d_ptr->last_updated = av_gettime_relative(); + } + } +} + +void Clock::reset(qint64 pos) +{ + QMutexLocker locker(&d_ptr->mutex); + d_ptr->pts = pos; + d_ptr->pts_drift = av_gettime_relative(); + d_ptr->last_updated = d_ptr->pts_drift; + d_ptr->paused = false; +} + +void Clock::update(qint64 pts, qint64 time) +{ + QMutexLocker locker(&d_ptr->mutex); + if (d_ptr->last_updated && !d_ptr->paused) { + // 计算时钟漂移量 + d_ptr->pts_drift += (pts - d_ptr->pts) * d_ptr->speed + (time - d_ptr->last_updated); + } + // 更新当前 AVFrame 的时间戳和上一次更新时钟状态的时间 + d_ptr->pts = pts; + d_ptr->last_updated = time; +} + +auto Clock::getClock(qint64 pts) -> qint64 +{ + Q_ASSERT(g_clock); + + QMutexLocker locker(&d_ptr->mutex); + qint64 time = av_gettime_relative(); + qint64 clock = d_ptr->pts_drift + pts * d_ptr->speed; + if (d_ptr->paused) { + clock = d_ptr->last_updated; + } else { + clock += time - d_ptr->last_updated; + } + // 计算全局时钟的状态与当前时钟的状态之间的差值 + return clock + g_clock->getPtsDrift() - getPtsDrift(); +} + +auto Clock::getDiff(qint64 &diff) -> bool +{ + qint64 clock = getClock(d_ptr->pts); + diff = clock - av_gettime_relative(); + if (diff <= -d_ptr->drop_threshold) { // 丢帧 + d_ptr->pts_drift = clock - d_ptr->pts; + return false; + } else if (diff <= d_ptr->sync_threshold) { + diff = 0; + } + + return true; +} + +void Clock::setMaster(Clock *clock) +{ + g_clock = clock; +} + +} // namespace Ffmpeg diff --git a/ffmpeg/clock.hpp b/ffmpeg/clock.hpp new file mode 100644 index 0000000..0fe6fcf --- /dev/null +++ b/ffmpeg/clock.hpp @@ -0,0 +1,57 @@ +#ifndef CLOCK_HPP +#define CLOCK_HPP + +#include + +namespace Ffmpeg { + +class Clock : public QObject +{ +public: + explicit Clock(QObject *parent = nullptr); + ~Clock() override; + + // 获取当前 AVFrame 的时间戳 + auto getPts() -> qint64; + + // 获取时钟漂移量 + auto getPtsDrift() -> qint64; + + // 获取上一次更新时钟状态的时间 + auto getLastUpdated() -> qint64; + + // 获取播放速度 + auto getSpeed() -> double; + + // 设置播放速度 + void setSpeed(double value); + + // 获取是否暂停播放 + auto isPaused() -> bool; + + // 设置是否暂停播放 + void setPaused(bool value); + + // 重置时钟状态 + void reset(qint64 pos); + + // 更新时钟状态 + void update(qint64 pts, qint64 time); + + // 获取当前时钟的状态 + auto getClock(qint64 pts) -> qint64; + + // 获取当前时钟的状态与上一次更新时钟状态的时间差 + auto getDiff(qint64 &diff) -> bool; + + // not thread safe + 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 \