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/audiodecoder.cpp b/ffmpeg/audiodecoder.cpp index 3b92878..19b42f4 100644 --- a/ffmpeg/audiodecoder.cpp +++ b/ffmpeg/audiodecoder.cpp @@ -32,27 +32,14 @@ AudioDecoder::AudioDecoder(QObject *parent) AudioDecoder::~AudioDecoder() = default; -bool AudioDecoder::seek(qint64 seekTime) -{ - if (!Decoder::seek(seekTime)) { - return false; - } - return d_ptr->decoderAudioFrame->seek(seekTime); -} - -void AudioDecoder::pause(bool state) -{ - d_ptr->decoderAudioFrame->pause(state); -} - void AudioDecoder::setVolume(qreal volume) { d_ptr->decoderAudioFrame->setVolume(volume); } -void AudioDecoder::setIsLocalFile(bool isLocalFile) +void AudioDecoder::setMasterClock() { - d_ptr->decoderAudioFrame->setIsLocalFile(isLocalFile); + d_ptr->decoderAudioFrame->setMasterClock(); } void AudioDecoder::runDecoder() diff --git a/ffmpeg/audiodecoder.h b/ffmpeg/audiodecoder.h index beb7a7b..b1602cd 100644 --- a/ffmpeg/audiodecoder.h +++ b/ffmpeg/audiodecoder.h @@ -13,13 +13,9 @@ class AudioDecoder : public Decoder explicit AudioDecoder(QObject *parent = nullptr); ~AudioDecoder() override; - bool seek(qint64 seekTime) override; - - void pause(bool state) override; - void setVolume(qreal volume); - void setIsLocalFile(bool isLocalFile); + void setMasterClock(); signals: void positionChanged(qint64 position); // ms diff --git a/ffmpeg/clock.cc b/ffmpeg/clock.cc new file mode 100644 index 0000000..6fb7624 --- /dev/null +++ b/ffmpeg/clock.cc @@ -0,0 +1,180 @@ +#include "clock.hpp" + +#include +#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 + qint64 serial = s_serial.load(); // 时钟序列号 for seek + bool paused = false; // 是否暂停播放 + + static std::atomic s_serial; + static constexpr QPair s_speedRange = {0.5, 3.0}; + static constexpr auto s_speedStep = 0.5; + static std::atomic s_speed; + static constexpr auto s_diffThreshold = 50 * 1000; // 50 milliseconds + static Clock *s_clock; +}; + +std::atomic Clock::ClockPrivate::s_serial = 0; +std::atomic Clock::ClockPrivate::s_speed = 1.0; +Clock *Clock::ClockPrivate::s_clock = nullptr; + +Clock::Clock(QObject *parent) + : QObject{parent} + , d_ptr(new ClockPrivate(this)) +{} + +Clock::~Clock() = default; + +void Clock::reset() +{ + QMutexLocker locker(&d_ptr->mutex); + d_ptr->pts = 0; + d_ptr->pts_drift = 0; + d_ptr->last_updated = av_gettime_relative(); + d_ptr->serial = Clock::ClockPrivate::s_serial.load(); + d_ptr->paused = false; +} + +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; +} + +void Clock::resetSerial() +{ + QMutexLocker locker(&d_ptr->mutex); + d_ptr->serial = Clock::ClockPrivate::s_serial.load(); +} + +auto Clock::serial() -> qint64 +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->serial; +} + +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) +{ + Q_ASSERT(Clock::ClockPrivate::s_clock); + + QMutexLocker locker(&d_ptr->mutex); + if (d_ptr->last_updated && !d_ptr->paused) { + if (this == Clock::ClockPrivate::s_clock) { + qint64 timediff = (time - d_ptr->last_updated) * speed(); + d_ptr->pts_drift += pts - d_ptr->pts - timediff; + } else { + auto *masterClock = Clock::ClockPrivate::s_clock; + auto masterClockPts = masterClock->d_ptr->pts - masterClock->d_ptr->pts_drift; + qint64 timediff = (time - masterClock->d_ptr->last_updated) * speed(); + d_ptr->pts_drift = pts - masterClockPts - timediff; + } + } + d_ptr->pts = pts; + d_ptr->last_updated = time; +} + +auto Clock::getDelayWithMaster(qint64 &delay) -> bool +{ + if (serial() != Clock::ClockPrivate::s_serial.load()) { + return false; + } + delay = ptsDrift(); + return true; +} + +auto Clock::adjustDelay(qint64 &delay) -> bool +{ + if (speed() > 1.0 && delay < 0) { + return false; + } else if (delay < -Clock::ClockPrivate::s_diffThreshold) { + return false; + } else if (qAbs(delay) <= Clock::ClockPrivate::s_diffThreshold) { + delay = 0; + return true; + } + delay -= Clock::ClockPrivate::s_diffThreshold; + return true; +} + +void Clock::globalSerialRef() +{ + Clock::ClockPrivate::s_serial.fetch_add(1); +} + +void Clock::globalSerialReset() +{ + Clock::ClockPrivate::s_serial.store(0); +} + +auto Clock::speedRange() -> QPair +{ + return Clock::ClockPrivate::s_speedRange; +} + +auto Clock::speedStep() -> double +{ + return Clock::ClockPrivate::s_speedStep; +} + +void Clock::setSpeed(double value) +{ + Clock::ClockPrivate::s_speed.store(value); +} + +auto Clock::speed() -> double +{ + return Clock::ClockPrivate::s_speed.load(); +} + +void Clock::setMaster(Clock *clock) +{ + Clock::ClockPrivate::s_clock = clock; +} + +} // namespace Ffmpeg diff --git a/ffmpeg/clock.hpp b/ffmpeg/clock.hpp new file mode 100644 index 0000000..8e17524 --- /dev/null +++ b/ffmpeg/clock.hpp @@ -0,0 +1,52 @@ +#ifndef CLOCK_HPP +#define CLOCK_HPP + +#include + +namespace Ffmpeg { + +class Clock : public QObject +{ +public: + explicit Clock(QObject *parent = nullptr); + ~Clock() override; + + void reset(); + + auto pts() -> qint64; + auto ptsDrift() -> qint64; + auto lastUpdated() -> qint64; + + void resetSerial(); + auto serial() -> qint64; + + void setPaused(bool value); + auto paused() -> bool; + + void update(qint64 pts, qint64 time); + + // return true if delay is valid + auto getDelayWithMaster(qint64 &delay) -> bool; + + // return true if delay is valid + static auto adjustDelay(qint64 &delay) -> bool; + + static void globalSerialRef(); + static void globalSerialReset(); + + static auto speedRange() -> QPair; + static 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/decoder.cc b/ffmpeg/decoder.cc index 3948cbf..955f995 100644 --- a/ffmpeg/decoder.cc +++ b/ffmpeg/decoder.cc @@ -38,29 +38,4 @@ void calculateTime(Packet *packet, AVContextInfo *contextInfo) // qDebug() << "Packet duration:" << duration << "pts:" << pts << "tb:" << tb.num << tb.den; } -static std::atomic g_meidaClock; - -void setMediaClock(qint64 value) -{ - g_meidaClock.store(value); -} - -auto mediaClock() -> qint64 -{ - return g_meidaClock.load(); -} - -std::atomic g_mediaSpeed = 1.0; - -void setMediaSpeed(double speed) -{ - Q_ASSERT(speed > 0); - g_mediaSpeed.store(speed); -} - -auto mediaSpeed() -> double -{ - return g_mediaSpeed.load(); -} - } // namespace Ffmpeg diff --git a/ffmpeg/decoder.h b/ffmpeg/decoder.h index 9caca1f..d5883cb 100644 --- a/ffmpeg/decoder.h +++ b/ffmpeg/decoder.h @@ -11,29 +11,21 @@ #include "formatcontext.h" #define Sleep_Queue_Full_Milliseconds 50 -#define UnWait_Microseconds (50 * 1000) -#define Drop_Microseconds (-100 * 1000) namespace Ffmpeg { -static const auto g_frameQueueSize = 25; +static const auto s_frameQueueSize = 25; void calculateTime(Frame *frame, AVContextInfo *contextInfo, FormatContext *formatContext); void calculateTime(Packet *packet, AVContextInfo *contextInfo); -void setMediaClock(qint64 value); -auto mediaClock() -> qint64; - -void setMediaSpeed(double speed); -auto mediaSpeed() -> double; - template class Decoder : public QThread { public: explicit Decoder(QObject *parent = nullptr) : QThread(parent) - , m_queue(g_frameQueueSize) + , m_queue(s_frameQueueSize) {} ~Decoder() override { stopDecoder(); } @@ -55,7 +47,6 @@ class Decoder : public QThread quit(); wait(); } - m_seekTime = 0; } void append(const T &t) { m_queue.put(t); } @@ -67,20 +58,6 @@ class Decoder : public QThread void wakeup() { m_queue.put(T()); } - virtual void pause(bool state) = 0; - - virtual bool seek(qint64 seekTime) // microsecond - { - clear(); - m_seekTime = seekTime; - assertVaild(); - if (!m_contextInfo->isIndexVaild()) { - return false; - } - pause(false); - return true; - } - protected: virtual void runDecoder() = 0; @@ -103,7 +80,6 @@ class Decoder : public QThread AVContextInfo *m_contextInfo = nullptr; FormatContext *m_formatContext = nullptr; std::atomic_bool m_runing = true; - qint64 m_seekTime = 0; // microsecond }; } // namespace Ffmpeg diff --git a/ffmpeg/decoderaudioframe.cpp b/ffmpeg/decoderaudioframe.cpp index b0cb7f1..19f7b19 100644 --- a/ffmpeg/decoderaudioframe.cpp +++ b/ffmpeg/decoderaudioframe.cpp @@ -1,5 +1,6 @@ #include "decoderaudioframe.h" #include "audioframeconverter.h" +#include "clock.hpp" #include "codeccontext.h" #include @@ -10,6 +11,10 @@ #include #include +extern "C" { +#include +} + namespace Ffmpeg { class DecoderAudioFrame::DecoderAudioFramePrivate @@ -17,7 +22,9 @@ class DecoderAudioFrame::DecoderAudioFramePrivate public: explicit DecoderAudioFramePrivate(DecoderAudioFrame *q) : q_ptr(q) - {} + { + clock = new Clock(q); + } ~DecoderAudioFramePrivate() = default; DecoderAudioFrame *q_ptr; @@ -26,18 +33,12 @@ class DecoderAudioFrame::DecoderAudioFramePrivate QIODevice *audioDevice = nullptr; qreal volume = 0.5; - std::atomic_bool pause = false; - double speed = 0; - qint64 pauseTime; + Clock *clock; QMutex mutex; QWaitCondition waitCondition; - qint64 seekTime = 0; // milliseconds - std::atomic_bool firstSeekFrame = false; - QScopedPointer mediaDevicesPtr; std::atomic_bool audioOutputsChanged = false; - std::atomic_bool isLocalFile = true; }; DecoderAudioFrame::DecoderAudioFrame(QObject *parent) @@ -49,40 +50,6 @@ DecoderAudioFrame::DecoderAudioFrame(QObject *parent) DecoderAudioFrame::~DecoderAudioFrame() = default; -void DecoderAudioFrame::stopDecoder() -{ - pause(false); - Decoder::stopDecoder(); -} - -bool DecoderAudioFrame::seek(qint64 seekTime) -{ - if (!Decoder::seek(seekTime)) { - return false; - } - setMediaClock(m_seekTime); - d_ptr->firstSeekFrame = true; - d_ptr->pauseTime = 0; - return true; -} - -void DecoderAudioFrame::pause(bool state) -{ - if (!isRunning()) { - return; - } - d_ptr->pause = state; - if (state) { - return; - } - d_ptr->waitCondition.wakeOne(); -} - -bool DecoderAudioFrame::isPause() -{ - return d_ptr->pause; -} - void DecoderAudioFrame::setVolume(qreal volume) { d_ptr->volume = volume; @@ -91,9 +58,9 @@ void DecoderAudioFrame::setVolume(qreal volume) } } -void DecoderAudioFrame::setIsLocalFile(bool isLocalFile) +void DecoderAudioFrame::setMasterClock() { - d_ptr->isLocalFile = isLocalFile; + Clock::setMaster(d_ptr->clock); } void DecoderAudioFrame::onStateChanged(QAudio::State state) @@ -128,102 +95,36 @@ void DecoderAudioFrame::runDecoder() quint64 dropNum = 0; QAudioDevice audioDevice(QMediaDevices::defaultAudioOutput()); auto format = resetAudioOutput(); - d_ptr->seekTime = 0; - d_ptr->pauseTime = 0; AudioFrameConverter audioConverter(m_contextInfo->codecCtx(), format); - QElapsedTimer timer; - if (d_ptr->isLocalFile) { // 音频播放依赖外部时钟,适用于本地文件播放 - while (m_runing) { - checkDefaultAudioOutput(audioDevice); - checkPause(d_ptr->pauseTime); - checkSpeed(timer, d_ptr->pauseTime); - - auto framePtr(m_queue.take()); - if (framePtr.isNull()) { - continue; - } - if (!timer.isValid()) { - timer.start(); - } - auto pts = framePtr->pts(); - if (m_seekTime > pts) { - continue; - } - if (d_ptr->firstSeekFrame) { - d_ptr->seekTime = pts; - d_ptr->firstSeekFrame = false; - timer.restart(); - } + d_ptr->clock->reset(); + while (m_runing.load()) { + checkDefaultAudioOutput(audioDevice); - auto audioBuf = audioConverter.convert(framePtr.data()); - auto diff = pts - d_ptr->seekTime - - (timer.elapsed() - d_ptr->pauseTime) * d_ptr->speed * 1000; - // qDebug() << "PTS:" - // << QTime::fromMSecsSinceStartOfDay(pts / 1000).toString("hh:mm:ss.zzz") - // << "SeekTime:" - // << QTime::fromMSecsSinceStartOfDay(d_ptr->seekTime / 1000) - // .toString("hh:mm:ss.zzz") - // << "Diff:" << diff; - { - auto cleanup = qScopeGuard([&] { - setMediaClock(pts); - emit positionChanged(pts); - }); - if (qAbs(diff) > UnWait_Microseconds && !d_ptr->pause) { - QMutexLocker locker(&d_ptr->mutex); - d_ptr->waitCondition.wait(&d_ptr->mutex, qAbs(diff) / 1000); - } else if (d_ptr->speed > 1.0) { - dropNum++; - continue; // speed > 1.0 drop - } - } - writeToDevice(audioBuf); + auto framePtr(m_queue.take()); + if (framePtr.isNull()) { + continue; } - } else { // 播放网络媒体 - QElapsedTimer durationTimer; - qint64 lastPts = 0; - while (m_runing) { - checkDefaultAudioOutput(audioDevice); - checkPause(d_ptr->pauseTime); - checkSpeed(timer, lastPts); - - auto framePtr(m_queue.take()); - if (framePtr.isNull()) { - continue; - } - auto pts = framePtr->pts(); - if (m_seekTime > pts) { - continue; - } - if (d_ptr->firstSeekFrame) { - d_ptr->seekTime = pts; - d_ptr->firstSeekFrame = false; - lastPts = 0; - } - - auto audioBuf = audioConverter.convert(framePtr.data()); - double diff = 0; - if (lastPts > 0) { - diff = pts - lastPts - - (durationTimer.elapsed() - d_ptr->pauseTime) * d_ptr->speed * 1000; - } - lastPts = pts; - { - auto cleanup = qScopeGuard([&] { - durationTimer.restart(); - setMediaClock(pts); - emit positionChanged(pts); - }); - if (diff > 0 && !d_ptr->pause) { - QMutexLocker locker(&d_ptr->mutex); - d_ptr->waitCondition.wait(&d_ptr->mutex, diff / 1000); - } else if (d_ptr->speed > 1.0) { - dropNum++; - continue; // speed > 1.0 drop - } - } - writeToDevice(audioBuf); + auto audioBuf = audioConverter.convert(framePtr.data()); + auto pts = framePtr->pts(); + auto emitPosition = qScopeGuard([=]() { emit positionChanged(pts); }); + d_ptr->clock->update(pts, av_gettime_relative()); + qint64 delay = 0; + if (!d_ptr->clock->getDelayWithMaster(delay)) { + continue; + } + if (!Clock::adjustDelay(delay)) { + dropNum++; + continue; } + delay /= 1000; + if (delay > 0) { + QMutexLocker locker(&d_ptr->mutex); + d_ptr->waitCondition.wait(&d_ptr->mutex, delay); + } + // qDebug() << "Audio PTS:" + // << QTime::fromMSecsSinceStartOfDay(pts / 1000).toString("hh:mm:ss.zzz"); + + writeToDevice(audioBuf); } d_ptr->audioSinkPtr.reset(); qInfo() << "Audio Drop Num:" << dropNum; @@ -251,41 +152,12 @@ void DecoderAudioFrame::checkDefaultAudioOutput(QAudioDevice &audioDevice) audioDevice = QMediaDevices::defaultAudioOutput(); } -void DecoderAudioFrame::checkPause(qint64 &pauseTime) -{ - if (!d_ptr->pause) { - return; - } - QElapsedTimer timerPause; - timerPause.start(); - while (d_ptr->pause) { - msleep(Sleep_Queue_Full_Milliseconds); - QMutexLocker locker(&d_ptr->mutex); - d_ptr->waitCondition.wait(&d_ptr->mutex); - } - if (timerPause.elapsed() > 100) { - pauseTime += timerPause.elapsed(); - } -} - -void DecoderAudioFrame::checkSpeed(QElapsedTimer &timer, qint64 &pauseTime) -{ - auto speed = mediaSpeed(); - if (d_ptr->speed == speed) { - return; - } - d_ptr->speed = speed; - d_ptr->seekTime = mediaClock() * 1000; - pauseTime = 0; - timer.restart(); -} - void DecoderAudioFrame::writeToDevice(QByteArray &audioBuf) { if (!d_ptr->audioDevice) { return; } - while (audioBuf.size() > 0 && !d_ptr->pause) { + while (audioBuf.size() > 0) { int byteFree = d_ptr->audioSinkPtr->bytesFree(); if (byteFree > 0 && byteFree < audioBuf.size()) { d_ptr->audioDevice->write(audioBuf.data(), byteFree); // Memory leak diff --git a/ffmpeg/decoderaudioframe.h b/ffmpeg/decoderaudioframe.h index 93a9f15..ae999a6 100644 --- a/ffmpeg/decoderaudioframe.h +++ b/ffmpeg/decoderaudioframe.h @@ -18,19 +18,12 @@ class DecoderAudioFrame : public Decoder explicit DecoderAudioFrame(QObject *parent = nullptr); ~DecoderAudioFrame(); - void stopDecoder() override; - - bool seek(qint64 seekTime) override; - - void pause(bool state) override; - auto isPause() -> bool; - void setVolume(qreal volume); - void setIsLocalFile(bool isLocalFile); + void setMasterClock(); signals: - void positionChanged(qint64 position); // ms + void positionChanged(qint64 position); // microsecond private slots: void onStateChanged(QAudio::State state); @@ -43,8 +36,6 @@ private slots: void buildConnect(); void checkDefaultAudioOutput(QAudioDevice &audioDevice); - void checkPause(qint64 &pauseTime); - void checkSpeed(QElapsedTimer &timer, qint64 &pauseTime); void writeToDevice(QByteArray &audioBuf); auto resetAudioOutput() -> QAudioFormat; diff --git a/ffmpeg/decodersubtitleframe.cc b/ffmpeg/decodersubtitleframe.cc index aa82595..f5ebd6f 100644 --- a/ffmpeg/decodersubtitleframe.cc +++ b/ffmpeg/decodersubtitleframe.cc @@ -1,4 +1,5 @@ #include "decodersubtitleframe.hpp" +#include "clock.hpp" #include "codeccontext.h" #include @@ -10,6 +11,7 @@ extern "C" { #include +#include #include } @@ -20,11 +22,14 @@ class DecoderSubtitleFrame::DecoderSubtitleFramePrivate public: explicit DecoderSubtitleFramePrivate(DecoderSubtitleFrame *q) : q_ptr(q) - {} + { + clock = new Clock(q); + } DecoderSubtitleFrame *q_ptr; - bool pause = false; + Clock *clock; + QMutex mutex; QWaitCondition waitCondition; QSize videoResolutionRatio = QSize(1280, 720); @@ -40,24 +45,6 @@ DecoderSubtitleFrame::DecoderSubtitleFrame(QObject *parent) DecoderSubtitleFrame::~DecoderSubtitleFrame() = default; -void DecoderSubtitleFrame::stopDecoder() -{ - pause(false); - Decoder::stopDecoder(); -} - -void DecoderSubtitleFrame::pause(bool state) -{ - if (!isRunning()) { - return; - } - d_ptr->pause = state; - if (state) { - return; - } - d_ptr->waitCondition.wakeOne(); -} - void DecoderSubtitleFrame::setVideoResolutionRatio(const QSize &size) { if (!size.isValid()) { @@ -73,6 +60,7 @@ void DecoderSubtitleFrame::setVideoRenders(QVector videoRenders) void DecoderSubtitleFrame::runDecoder() { + quint64 dropNum = 0; auto ctx = m_contextInfo->codecCtx()->avCodecCtx(); QScopedPointer assPtr(new Ass); if (ctx->subtitle_header) { @@ -80,51 +68,41 @@ void DecoderSubtitleFrame::runDecoder() } assPtr->setWindowSize(d_ptr->videoResolutionRatio); SwsContext *swsContext = nullptr; - quint64 dropNum = 0; - while (m_runing) { - checkPause(); - + d_ptr->clock->reset(); + while (m_runing.load()) { auto subtitlePtr(m_queue.take()); if (subtitlePtr.isNull()) { continue; } subtitlePtr->setVideoResolutionRatio(d_ptr->videoResolutionRatio); subtitlePtr->parse(swsContext); - auto pts = subtitlePtr->pts(); - auto duration = subtitlePtr->duration(); - if (m_seekTime > (pts + duration)) { - assPtr->flushASSEvents(); - continue; - } if (subtitlePtr->type() == Subtitle::Type::ASS) { subtitlePtr->resolveAss(assPtr.data()); subtitlePtr->generateImage(); } - auto diffPts = pts - mediaClock(); - auto difDuration = diffPts + duration; - if (difDuration < Drop_Microseconds - || (mediaSpeed() > 1.0 && qAbs(difDuration) > UnWait_Microseconds)) { + d_ptr->clock->update(subtitlePtr->pts(), av_gettime_relative()); + qint64 delay = 0; + if (!d_ptr->clock->getDelayWithMaster(delay)) { + continue; + } + auto delayDuration = delay + subtitlePtr->duration(); + if (!Clock::adjustDelay(delayDuration)) { dropNum++; continue; - } else if (diffPts > UnWait_Microseconds && !d_ptr->pause) { - QMutexLocker locker(&d_ptr->mutex); - d_ptr->waitCondition.wait(&d_ptr->mutex, diffPts / 1000); } - // 略慢于音频 + if (Clock::adjustDelay(delay)) { + if (delay > 0) { + QMutexLocker locker(&d_ptr->mutex); + d_ptr->waitCondition.wait(&d_ptr->mutex, delay / 1000); + } + } + renderFrame(subtitlePtr); } sws_freeContext(swsContext); qInfo() << "Subtitle Drop Num:" << dropNum; } -void DecoderSubtitleFrame::checkPause() -{ - while (d_ptr->pause) { - QMutexLocker locker(&d_ptr->mutex); - d_ptr->waitCondition.wait(&d_ptr->mutex); - } -} - void DecoderSubtitleFrame::renderFrame(const QSharedPointer &subtitlePtr) { QMutexLocker locker(&d_ptr->mutex_render); diff --git a/ffmpeg/decodersubtitleframe.hpp b/ffmpeg/decodersubtitleframe.hpp index e46933b..8bb8482 100644 --- a/ffmpeg/decodersubtitleframe.hpp +++ b/ffmpeg/decodersubtitleframe.hpp @@ -15,10 +15,6 @@ class DecoderSubtitleFrame : public Decoder explicit DecoderSubtitleFrame(QObject *parent = nullptr); ~DecoderSubtitleFrame(); - void stopDecoder() override; - - void pause(bool state) override; - void setVideoResolutionRatio(const QSize &size); void setVideoRenders(QVector videoRenders); @@ -27,7 +23,6 @@ class DecoderSubtitleFrame : public Decoder void runDecoder() override; private: - void checkPause(); void renderFrame(const QSharedPointer &subtitlePtr); class DecoderSubtitleFramePrivate; diff --git a/ffmpeg/decodervideoframe.cpp b/ffmpeg/decodervideoframe.cpp index 8f2a20a..e4bd8f9 100644 --- a/ffmpeg/decodervideoframe.cpp +++ b/ffmpeg/decodervideoframe.cpp @@ -1,10 +1,16 @@ #include "decodervideoframe.h" +#include "clock.hpp" #include #include +#include #include +extern "C" { +#include +} + namespace Ffmpeg { class DecoderVideoFrame::DecoderVideoFramePrivate @@ -12,11 +18,14 @@ class DecoderVideoFrame::DecoderVideoFramePrivate public: explicit DecoderVideoFramePrivate(DecoderVideoFrame *q) : q_ptr(q) - {} + { + clock = new Clock(q); + } DecoderVideoFrame *q_ptr; - bool pause = false; + Clock *clock; + QMutex mutex; QWaitCondition waitCondition; @@ -31,69 +40,52 @@ DecoderVideoFrame::DecoderVideoFrame(QObject *parent) DecoderVideoFrame::~DecoderVideoFrame() {} -void DecoderVideoFrame::stopDecoder() -{ - pause(false); - Decoder::stopDecoder(); -} - -void DecoderVideoFrame::pause(bool state) -{ - if (!isRunning()) { - return; - } - d_ptr->pause = state; - if (state) { - return; - } - d_ptr->waitCondition.wakeOne(); -} - void DecoderVideoFrame::setVideoRenders(QVector videoRenders) { QMutexLocker locker(&d_ptr->mutex_render); d_ptr->videoRenders = videoRenders; } +void DecoderVideoFrame::setMasterClock() +{ + Clock::setMaster(d_ptr->clock); +} + void DecoderVideoFrame::runDecoder() { for (auto render : d_ptr->videoRenders) { render->resetFps(); } quint64 dropNum = 0; - while (m_runing) { - checkPause(); - + d_ptr->clock->reset(); + while (m_runing.load()) { auto framePtr(m_queue.take()); if (framePtr.isNull()) { continue; } auto pts = framePtr->pts(); - if (m_seekTime > pts) { + auto emitPosition = qScopeGuard([=]() { emit positionChanged(pts); }); + d_ptr->clock->update(pts, av_gettime_relative()); + qint64 delay = 0; + if (!d_ptr->clock->getDelayWithMaster(delay)) { continue; } - auto diff = pts - mediaClock(); - if (diff < Drop_Microseconds || (mediaSpeed() > 1.0 && qAbs(diff) > UnWait_Microseconds)) { + if (!Clock::adjustDelay(delay)) { dropNum++; continue; - } else if (diff > UnWait_Microseconds && !d_ptr->pause) { + } + if (delay > 0) { QMutexLocker locker(&d_ptr->mutex); - d_ptr->waitCondition.wait(&d_ptr->mutex, diff / 1000); + d_ptr->waitCondition.wait(&d_ptr->mutex, delay / 1000); } - // 略慢于音频 + // qDebug() << "Video PTS:" + // << QTime::fromMSecsSinceStartOfDay(pts / 1000).toString("hh:mm:ss.zzz"); + renderFrame(framePtr); } qInfo() << "Video Drop Num:" << dropNum; } -void DecoderVideoFrame::checkPause() -{ - while (d_ptr->pause) { - QMutexLocker locker(&d_ptr->mutex); - d_ptr->waitCondition.wait(&d_ptr->mutex); - } -} - void DecoderVideoFrame::renderFrame(const QSharedPointer &framePtr) { QMutexLocker locker(&d_ptr->mutex_render); diff --git a/ffmpeg/decodervideoframe.h b/ffmpeg/decodervideoframe.h index 3693ddb..6ad2173 100644 --- a/ffmpeg/decodervideoframe.h +++ b/ffmpeg/decodervideoframe.h @@ -10,21 +10,22 @@ class VideoRender; class DecoderVideoFrame : public Decoder { + Q_OBJECT public: explicit DecoderVideoFrame(QObject *parent = nullptr); ~DecoderVideoFrame(); - void stopDecoder() override; + void setVideoRenders(QVector videoRenders); - void pause(bool state) override; + void setMasterClock(); - void setVideoRenders(QVector videoRenders); +signals: + void positionChanged(qint64 position); // microsecond protected: void runDecoder() override; private: - void checkPause(); void renderFrame(const QSharedPointer &framePtr); class DecoderVideoFramePrivate; 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 \ diff --git a/ffmpeg/player.cpp b/ffmpeg/player.cpp index e9058cd..ac535e3 100644 --- a/ffmpeg/player.cpp +++ b/ffmpeg/player.cpp @@ -24,20 +24,181 @@ namespace Ffmpeg { class Player::PlayerPrivate { public: - PlayerPrivate(QObject *parent) - : owner(parent) + PlayerPrivate(Player *q) + : q_ptr(q) { - formatCtx = new FormatContext(owner); + formatCtx = new FormatContext(q_ptr); - audioInfo = new AVContextInfo(owner); - videoInfo = new AVContextInfo(owner); - subtitleInfo = new AVContextInfo(owner); + audioInfo = new AVContextInfo(q_ptr); + videoInfo = new AVContextInfo(q_ptr); + subtitleInfo = new AVContextInfo(q_ptr); - audioDecoder = new AudioDecoder(owner); - videoDecoder = new VideoDecoder(owner); - subtitleDecoder = new SubtitleDecoder(owner); + audioDecoder = new AudioDecoder(q_ptr); + videoDecoder = new VideoDecoder(q_ptr); + subtitleDecoder = new SubtitleDecoder(q_ptr); } - QObject *owner; + + auto initAvCodec() -> bool + { + isopen = false; + + //初始化pFormatCtx结构 + if (!formatCtx->openFilePath(filepath)) { + return false; + } + + //获取音视频流数据信息 + if (!formatCtx->findStream()) { + return false; + } + + emit q_ptr->durationChanged(formatCtx->duration()); + q_ptr->onPositionChanged(0); + + audioInfo->resetIndex(); + auto audioTracks = formatCtx->audioMap(); + if (!audioTracks.isEmpty()) { + int audioIndex = formatCtx->findBestStreamIndex(AVMEDIA_TYPE_AUDIO); + if (!setMediaIndex(audioInfo, audioIndex)) { + return false; + } + emit q_ptr->audioTracksChanged(audioTracks.values()); + emit q_ptr->audioTrackChanged(audioTracks.value(audioIndex)); + } + + if (!formatCtx->coverImage().isNull()) { + for (auto render : videoRenders) { + render->setImage(formatCtx->coverImage()); + } + } + + videoInfo->resetIndex(); + auto videoIndexs = formatCtx->videoIndexs(); + if (!videoIndexs.isEmpty()) { + int videoIndex = videoIndexs.first(); + if (!setMediaIndex(videoInfo, videoIndex)) { + videoInfo->resetIndex(); + } + } + + subtitleInfo->resetIndex(); + auto subtitleTracks = formatCtx->subtitleMap(); + if (!subtitleTracks.isEmpty()) { + int subtitleIndex = formatCtx->findBestStreamIndex(AVMEDIA_TYPE_SUBTITLE); + if (!setMediaIndex(subtitleInfo, subtitleIndex)) { + return false; + } + subtitleDecoder->setVideoResolutionRatio(resolutionRatio()); + emit q_ptr->subTracksChanged(subtitleTracks.values()); + emit q_ptr->subTrackChanged(subtitleTracks.value(subtitleIndex)); + } + + qDebug() << audioInfo->index() << videoInfo->index() << subtitleInfo->index(); + if (!audioInfo->isIndexVaild() && !videoInfo->isIndexVaild()) { + return false; + } + + isopen = true; + // formatCtx->printFileInfo(); + formatCtx->dumpFormat(); + return true; + } + + void playVideo() + { + Q_ASSERT(isopen); + + formatCtx->discardStreamExcluded( + {audioInfo->index(), videoInfo->index(), subtitleInfo->index()}); + formatCtx->seekFirstFrame(); + + videoDecoder->startDecoder(formatCtx, videoInfo); + subtitleDecoder->startDecoder(formatCtx, subtitleInfo); + audioDecoder->startDecoder(formatCtx, audioInfo); + + if (audioInfo->isIndexVaild()) { + audioDecoder->setMasterClock(); + } else if (videoInfo->isIndexVaild()) { + videoDecoder->setMasterClock(); + } else { + Q_ASSERT(false); + } + + emit q_ptr->playStarted(); + + qint64 readSize = 0; + QElapsedTimer elapsedTimer; + elapsedTimer.start(); + + while (runing) { + PacketPtr packetPtr(new Packet); + if (!formatCtx->readFrame(packetPtr.data())) { + break; + } + + calculateSpeed(elapsedTimer, readSize, packetPtr->avPacket()->size); + + auto stream_index = packetPtr->streamIndex(); + if (!formatCtx->checkPktPlayRange(packetPtr.data())) { + } else if (audioInfo->isIndexVaild() + && stream_index == audioInfo->index()) { // 如果是音频数据 + audioDecoder->append(packetPtr); + } else if (videoInfo->isIndexVaild() && stream_index == videoInfo->index() + && !(videoInfo->stream()->disposition + & AV_DISPOSITION_ATTACHED_PIC)) { // 如果是视频数据 + videoDecoder->append(packetPtr); + } else if (subtitleInfo->isIndexVaild() + && stream_index == subtitleInfo->index()) { // 如果是字幕数据 + subtitleDecoder->append(packetPtr); + } + } + while (runing && (videoDecoder->size() > 0 || audioDecoder->size() > 0)) { + msleep(Sleep_Queue_Full_Milliseconds); + } + subtitleDecoder->stopDecoder(); + videoDecoder->stopDecoder(); + audioDecoder->stopDecoder(); + + qInfo() << "play finish"; + } + + auto setMediaIndex(AVContextInfo *contextInfo, int index) -> bool + { + contextInfo->setIndex(index); + contextInfo->setStream(formatCtx->stream(index)); + if (!contextInfo->initDecoder(formatCtx->guessFrameRate(index))) { + return false; + } + return contextInfo->openCodec(gpuDecode ? AVContextInfo::GpuType::GpuDecode + : AVContextInfo::GpuType::NotUseGpu); + } + + void setMediaState(MediaState mediaState) + { + mediaState = mediaState; + emit q_ptr->stateChanged(mediaState); + } + + QSize resolutionRatio() const + { + return videoInfo->isIndexVaild() ? videoInfo->resolutionRatio() : QSize(); + } + + void calculateSpeed(QElapsedTimer &timer, qint64 &readSize, qint64 addSize) + { + readSize += addSize; + auto elapsed = timer.elapsed(); + if (elapsed < 5000) { + return; + } + + auto speed = readSize * 1000 / elapsed; + emit q_ptr->readSpeedChanged(speed); + timer.restart(); + readSize = 0; + } + + Player *q_ptr; FormatContext *formatCtx; AVContextInfo *audioInfo; @@ -51,9 +212,8 @@ class Player::PlayerPrivate QString filepath; std::atomic_bool isopen = true; std::atomic_bool runing = true; - std::atomic_bool seek = false; - qint64 seekTime = 0; // seconds bool gpuDecode = false; + qint64 position = 0; std::atomic mediaState = Player::MediaState::StoppedState; @@ -84,8 +244,7 @@ void Player::openMedia(const QString &filepath) { onStop(); d_ptr->filepath = filepath; - d_ptr->audioDecoder->setIsLocalFile(QFile::exists(filepath)); - initAvCodec(); + d_ptr->initAvCodec(); if (!d_ptr->isopen) { qWarning() << "initAvCode Error"; return; @@ -96,7 +255,7 @@ void Player::openMedia(const QString &filepath) void Player::onPlay() { buildConnect(true); - setMediaState(MediaState::PlayingState); + d_ptr->setMediaState(MediaState::PlayingState); d_ptr->runing = true; start(); } @@ -119,22 +278,23 @@ void Player::onStop() render->resetAllFrame(); } d_ptr->formatCtx->close(); - setMediaState(MediaState::StoppedState); + d_ptr->setMediaState(MediaState::StoppedState); +} + +void Player::onPositionChanged(qint64 position) +{ + auto diff = (position - d_ptr->position) / AV_TIME_BASE; + if (qAbs(diff) < 1) { + return; + } + d_ptr->position = position; + emit positionChanged(position); } void Player::seek(qint64 position) { qInfo() << "Seek To: " << QTime::fromMSecsSinceStartOfDay(position / 1000).toString("hh:mm:ss.zzz"); - - if (d_ptr->seek) { - return; - } - if (isRunning()) { - blockSignals(true); - } - d_ptr->seek = true; - d_ptr->seekTime = position; } void Player::setAudioTrack(const QString &text) // 停止再播放最简单 之后在优化 @@ -148,18 +308,18 @@ void Player::setAudioTrack(const QString &text) // 停止再播放最简单 之 int subtitleIndex = d_ptr->subtitleInfo->index(); onStop(); - initAvCodec(); + d_ptr->initAvCodec(); if (subtitleIndex >= 0) { - if (!setMediaIndex(d_ptr->subtitleInfo, subtitleIndex)) { + if (!d_ptr->setMediaIndex(d_ptr->subtitleInfo, subtitleIndex)) { return; } emit subTrackChanged(d_ptr->formatCtx->subtitleMap().value(subtitleIndex)); } - if (!setMediaIndex(d_ptr->audioInfo, index)) { + if (!d_ptr->setMediaIndex(d_ptr->audioInfo, index)) { return; } emit audioTrackChanged(text); - seek(mediaClock()); + seek(d_ptr->position); onPlay(); } @@ -174,18 +334,18 @@ void Player::setSubtitleTrack(const QString &text) int audioIndex = d_ptr->audioInfo->index(); onStop(); - initAvCodec(); + d_ptr->initAvCodec(); if (audioIndex >= 0) { - if (!setMediaIndex(d_ptr->audioInfo, audioIndex)) { + if (!d_ptr->setMediaIndex(d_ptr->audioInfo, audioIndex)) { return; } emit audioTrackChanged(d_ptr->formatCtx->audioMap().value(audioIndex)); } - if (!setMediaIndex(d_ptr->subtitleInfo, index)) { + if (!d_ptr->setMediaIndex(d_ptr->subtitleInfo, index)) { return; } emit subTrackChanged(text); - seek(mediaClock()); + seek(d_ptr->position); onPlay(); } @@ -208,200 +368,21 @@ void Player::setSpeed(double speed) qWarning() << tr("Speed cannot be less than or equal to 0!"); return; } - setMediaSpeed(speed); } double Player::speed() { - return mediaSpeed(); -} - -bool Player::initAvCodec() -{ - d_ptr->isopen = false; - - //初始化pFormatCtx结构 - if (!d_ptr->formatCtx->openFilePath(d_ptr->filepath)) { - return false; - } - - //获取音视频流数据信息 - if (!d_ptr->formatCtx->findStream()) { - return false; - } - - emit durationChanged(d_ptr->formatCtx->duration()); - emit positionChanged(0); - - d_ptr->audioInfo->resetIndex(); - QMap audioTracks = d_ptr->formatCtx->audioMap(); - if (!audioTracks.isEmpty()) { - int audioIndex = d_ptr->formatCtx->findBestStreamIndex(AVMEDIA_TYPE_AUDIO); - if (!setMediaIndex(d_ptr->audioInfo, audioIndex)) { - return false; - } - emit audioTracksChanged(audioTracks.values()); - emit audioTrackChanged(audioTracks.value(audioIndex)); - } - - if (!d_ptr->formatCtx->coverImage().isNull()) { - for (auto render : d_ptr->videoRenders) { - render->setImage(d_ptr->formatCtx->coverImage()); - } - } - - d_ptr->videoInfo->resetIndex(); - auto videoIndexs = d_ptr->formatCtx->videoIndexs(); - if (!videoIndexs.isEmpty()) { - int videoIndex = videoIndexs.first(); - if (!setMediaIndex(d_ptr->videoInfo, videoIndex)) { - d_ptr->videoInfo->resetIndex(); - } - } - - d_ptr->subtitleInfo->resetIndex(); - QMap subtitleTracks = d_ptr->formatCtx->subtitleMap(); - if (!subtitleTracks.isEmpty()) { - int subtitleIndex = d_ptr->formatCtx->findBestStreamIndex(AVMEDIA_TYPE_SUBTITLE); - if (!setMediaIndex(d_ptr->subtitleInfo, subtitleIndex)) { - return false; - } - d_ptr->subtitleDecoder->setVideoResolutionRatio(resolutionRatio()); - emit subTracksChanged(subtitleTracks.values()); - emit subTrackChanged(subtitleTracks.value(subtitleIndex)); - } - - qDebug() << d_ptr->audioInfo->index() << d_ptr->videoInfo->index() - << d_ptr->subtitleInfo->index(); - if (!d_ptr->audioInfo->isIndexVaild() && !d_ptr->videoInfo->isIndexVaild()) { - return false; - } - - d_ptr->isopen = true; - d_ptr->formatCtx->printFileInfo(); - d_ptr->formatCtx->dumpFormat(); - return true; -} - -void Player::playVideo() -{ - d_ptr->formatCtx->discardStreamExcluded( - {d_ptr->audioInfo->index(), d_ptr->videoInfo->index(), d_ptr->subtitleInfo->index()}); - d_ptr->formatCtx->seekFirstFrame(); - - d_ptr->videoDecoder->startDecoder(d_ptr->formatCtx, d_ptr->videoInfo); - d_ptr->subtitleDecoder->startDecoder(d_ptr->formatCtx, d_ptr->subtitleInfo); - d_ptr->audioDecoder->startDecoder(d_ptr->formatCtx, d_ptr->audioInfo); - - setMediaClock(0); - emit playStarted(); - - qint64 readSize = 0; - QElapsedTimer elapsedTimer; - elapsedTimer.start(); - - while (d_ptr->runing) { - checkSeek(); - - PacketPtr packetPtr(new Packet); - if (!d_ptr->formatCtx->readFrame(packetPtr.data())) { - break; - } - - calculateSpeed(elapsedTimer, readSize, packetPtr->avPacket()->size); - - auto stream_index = packetPtr->streamIndex(); - if (!d_ptr->formatCtx->checkPktPlayRange(packetPtr.data())) { - } else if (d_ptr->audioInfo->isIndexVaild() - && stream_index == d_ptr->audioInfo->index()) { // 如果是音频数据 - d_ptr->audioDecoder->append(packetPtr); - } else if (d_ptr->videoInfo->isIndexVaild() && stream_index == d_ptr->videoInfo->index() - && !(d_ptr->videoInfo->stream()->disposition - & AV_DISPOSITION_ATTACHED_PIC)) { // 如果是视频数据 - d_ptr->videoDecoder->append(packetPtr); - } else if (d_ptr->subtitleInfo->isIndexVaild() - && stream_index == d_ptr->subtitleInfo->index()) { // 如果是字幕数据 - d_ptr->subtitleDecoder->append(packetPtr); - } - } - while (d_ptr->runing && (d_ptr->videoDecoder->size() > 0 || d_ptr->audioDecoder->size() > 0)) { - msleep(Sleep_Queue_Full_Milliseconds); - } - d_ptr->subtitleDecoder->stopDecoder(); - d_ptr->videoDecoder->stopDecoder(); - d_ptr->audioDecoder->stopDecoder(); - - qInfo() << "play finish"; -} - -void Player::checkSeek() -{ - if (!d_ptr->seek) { - return; - } - QElapsedTimer timer; - timer.start(); - - d_ptr->videoDecoder->seek(d_ptr->seekTime); - d_ptr->audioDecoder->seek(d_ptr->seekTime); - d_ptr->subtitleDecoder->seek(d_ptr->seekTime); - d_ptr->formatCtx->seek(d_ptr->seekTime); - if (d_ptr->videoInfo->isIndexVaild()) { - d_ptr->videoInfo->codecCtx()->flush(); - } - if (d_ptr->audioInfo->isIndexVaild()) { - d_ptr->audioInfo->codecCtx()->flush(); - } - if (d_ptr->subtitleInfo->isIndexVaild()) { - d_ptr->subtitleInfo->codecCtx()->flush(); - } - d_ptr->seek = false; - blockSignals(false); - setMediaState(MediaState::PlayingState); - qInfo() << "Seek ElapsedTimer: " << timer.elapsed(); - emit seekFinished(); -} - -void Player::setMediaState(Player::MediaState mediaState) -{ - d_ptr->mediaState = mediaState; - emit stateChanged(d_ptr->mediaState); -} - -bool Player::setMediaIndex(AVContextInfo *contextInfo, int index) -{ - contextInfo->setIndex(index); - contextInfo->setStream(d_ptr->formatCtx->stream(index)); - if (!contextInfo->initDecoder(d_ptr->formatCtx->guessFrameRate(index))) { - return false; - } - return contextInfo->openCodec(d_ptr->gpuDecode ? AVContextInfo::GpuType::GpuDecode - : AVContextInfo::GpuType::NotUseGpu); -} - -void Player::calculateSpeed(QElapsedTimer &timer, qint64 &readSize, qint64 addSize) -{ - readSize += addSize; - auto elapsed = timer.elapsed(); - if (elapsed > 5000) { - auto speed = readSize * 1000 / elapsed; - emit readSpeedChanged(speed); - timer.restart(); - readSize = 0; - } + return 1.0; } void Player::pause(bool status) { - d_ptr->audioDecoder->pause(status); - d_ptr->videoDecoder->pause(status); - d_ptr->subtitleDecoder->pause(status); if (status) { - setMediaState(MediaState::PausedState); + d_ptr->setMediaState(MediaState::PausedState); } else if (isRunning()) { - setMediaState(MediaState::PlayingState); + d_ptr->setMediaState(MediaState::PlayingState); } else { - setMediaState(MediaState::StoppedState); + d_ptr->setMediaState(MediaState::StoppedState); } } @@ -414,20 +395,20 @@ void Player::setUseGpuDecode(bool on) int audioIndex = d_ptr->audioInfo->index(); int subtitleIndex = d_ptr->subtitleInfo->index(); onStop(); - initAvCodec(); + d_ptr->initAvCodec(); if (audioIndex >= 0) { - if (!setMediaIndex(d_ptr->audioInfo, audioIndex)) { + if (!d_ptr->setMediaIndex(d_ptr->audioInfo, audioIndex)) { return; } emit audioTrackChanged(d_ptr->formatCtx->audioMap().value(audioIndex)); } if (subtitleIndex >= 0) { - if (!setMediaIndex(d_ptr->subtitleInfo, subtitleIndex)) { + if (!d_ptr->setMediaIndex(d_ptr->subtitleInfo, subtitleIndex)) { return; } emit subTrackChanged(d_ptr->formatCtx->subtitleMap().value(subtitleIndex)); } - seek(mediaClock()); + seek(d_ptr->position); onPlay(); } @@ -448,7 +429,7 @@ qint64 Player::duration() const qint64 Player::position() const { - return mediaClock(); + return d_ptr->position; } qint64 Player::fames() const @@ -458,7 +439,7 @@ qint64 Player::fames() const QSize Player::resolutionRatio() const { - return d_ptr->videoInfo->isIndexVaild() ? d_ptr->videoInfo->resolutionRatio() : QSize(); + return d_ptr->resolutionRatio(); } double Player::fps() const @@ -498,7 +479,7 @@ void Player::run() if (!d_ptr->isopen) { return; } - playVideo(); + d_ptr->playVideo(); } void Player::buildConnect(bool state) @@ -507,13 +488,22 @@ void Player::buildConnect(bool state) connect(d_ptr->audioDecoder, &AudioDecoder::positionChanged, this, - &Player::positionChanged, + &Player::onPositionChanged, + Qt::UniqueConnection); + connect(d_ptr->videoDecoder, + &VideoDecoder::positionChanged, + this, + &Player::onPositionChanged, Qt::UniqueConnection); } else { disconnect(d_ptr->audioDecoder, &AudioDecoder::positionChanged, this, - &Player::positionChanged); + &Player::onPositionChanged); + disconnect(d_ptr->videoDecoder, + &VideoDecoder::positionChanged, + this, + &Player::onPositionChanged); } } diff --git a/ffmpeg/player.h b/ffmpeg/player.h index 70ab3ca..d57f7c8 100644 --- a/ffmpeg/player.h +++ b/ffmpeg/player.h @@ -59,6 +59,9 @@ public slots: void onPlay(); void onStop(); +private slots: + void onPositionChanged(qint64 position); + signals: void durationChanged(qint64 duration); // microsecond void positionChanged(qint64 position); // microsecond @@ -80,12 +83,6 @@ public slots: private: void buildConnect(bool state = true); void buildErrorConnect(); - auto initAvCodec() -> bool; - void playVideo(); - void checkSeek(); - void setMediaState(MediaState mediaState); - auto setMediaIndex(AVContextInfo *contextInfo, int index) -> bool; - void calculateSpeed(QElapsedTimer &timer, qint64 &readSize, qint64 addSize); class PlayerPrivate; QScopedPointer d_ptr; diff --git a/ffmpeg/subtitledecoder.cpp b/ffmpeg/subtitledecoder.cpp index e8eda32..7055e5f 100644 --- a/ffmpeg/subtitledecoder.cpp +++ b/ffmpeg/subtitledecoder.cpp @@ -29,19 +29,6 @@ SubtitleDecoder::SubtitleDecoder(QObject *parent) SubtitleDecoder::~SubtitleDecoder() = default; -bool SubtitleDecoder::seek(qint64 seekTime) -{ - if (!Decoder::seek(seekTime)) { - return false; - } - return d_ptr->decoderSubtitleFrame->seek(seekTime); -} - -void SubtitleDecoder::pause(bool state) -{ - d_ptr->decoderSubtitleFrame->pause(state); -} - void SubtitleDecoder::setVideoResolutionRatio(const QSize &size) { d_ptr->decoderSubtitleFrame->setVideoResolutionRatio(size); diff --git a/ffmpeg/subtitledecoder.h b/ffmpeg/subtitledecoder.h index 9b74e9b..86f0f61 100644 --- a/ffmpeg/subtitledecoder.h +++ b/ffmpeg/subtitledecoder.h @@ -14,10 +14,6 @@ class SubtitleDecoder : public Decoder explicit SubtitleDecoder(QObject *parent = nullptr); ~SubtitleDecoder(); - bool seek(qint64 seekTime) override; - - void pause(bool state) override; - void setVideoResolutionRatio(const QSize &size); void setVideoRenders(QVector videoRenders); diff --git a/ffmpeg/videodecoder.cpp b/ffmpeg/videodecoder.cpp index 96bf2f7..d726584 100644 --- a/ffmpeg/videodecoder.cpp +++ b/ffmpeg/videodecoder.cpp @@ -24,28 +24,25 @@ class VideoDecoder::VideoDecoderPrivate VideoDecoder::VideoDecoder(QObject *parent) : Decoder(parent) , d_ptr(new VideoDecoderPrivate(this)) -{} - -VideoDecoder::~VideoDecoder() = default; - -bool VideoDecoder::seek(qint64 seekTime) { - if (!Decoder::seek(seekTime)) { - return false; - } - return d_ptr->decoderVideoFrame->seek(seekTime); + connect(d_ptr->decoderVideoFrame, + &DecoderVideoFrame::positionChanged, + this, + &VideoDecoder::positionChanged); } -void VideoDecoder::pause(bool state) -{ - d_ptr->decoderVideoFrame->pause(state); -} +VideoDecoder::~VideoDecoder() = default; void VideoDecoder::setVideoRenders(QVector videoRenders) { d_ptr->decoderVideoFrame->setVideoRenders(videoRenders); } +void VideoDecoder::setMasterClock() +{ + d_ptr->decoderVideoFrame->setMasterClock(); +} + void VideoDecoder::runDecoder() { d_ptr->decoderVideoFrame->startDecoder(m_formatContext, m_contextInfo); diff --git a/ffmpeg/videodecoder.h b/ffmpeg/videodecoder.h index 84059a8..2c8afb1 100644 --- a/ffmpeg/videodecoder.h +++ b/ffmpeg/videodecoder.h @@ -10,15 +10,17 @@ class VideoRender; class VideoDecoder : public Decoder { + Q_OBJECT public: explicit VideoDecoder(QObject *parent = nullptr); ~VideoDecoder(); - bool seek(qint64 seekTime) override; + void setVideoRenders(QVector videoRenders); - void pause(bool state) override; + void setMasterClock(); - void setVideoRenders(QVector videoRenders); +signals: + void positionChanged(qint64 position); // microsecond protected: void runDecoder() override; diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 1458132..b03d615 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -10,6 +10,7 @@ set(PROJECT_SOURCES logasync.h osspecificaspects.h singleton.hpp + threadsafequeue.hpp utils_global.h utils.cpp utils.h) diff --git a/utils/threadsafequeue.hpp b/utils/threadsafequeue.hpp new file mode 100644 index 0000000..a40a4dc --- /dev/null +++ b/utils/threadsafequeue.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +namespace Utils { + +template +class ThreadSafeQueue +{ + Q_DISABLE_COPY_MOVE(ThreadSafeQueue); + +public: + using ClearCallback = std::function; + + explicit ThreadSafeQueue() = default; + + void put(const T &x) + { + QMutexLocker locker(&m_mutex); + m_queue.push(x); + } + + void put(T &&x) + { + QMutexLocker locker(&m_mutex); + m_queue.push(std::move(x)); + } + + auto take() -> T + { + QMutexLocker locker(&m_mutex); + if (m_queue.empty()) { + return T(); + } + T front(std::move(m_queue.front())); + m_queue.pop(); + return front; + } + + void clear(ClearCallback callback = nullptr) + { + QMutexLocker locker(&m_mutex); + while (!m_queue.empty()) { + if (callback != nullptr) { + callback(m_queue.front()); + } + m_queue.pop(); + } + } + + auto size() const -> int + { + QMutexLocker locker(&m_mutex); + return m_queue.size(); + } + + auto empty() const -> bool + { + QMutexLocker locker(&m_mutex); + return m_queue.empty(); + } + +private: + mutable QMutex m_mutex; + std::queue m_queue; +}; + +} // namespace Utils diff --git a/utils/utils.pro b/utils/utils.pro index fd1bc3c..a7892b0 100644 --- a/utils/utils.pro +++ b/utils/utils.pro @@ -24,5 +24,6 @@ HEADERS += \ logasync.h \ osspecificaspects.h \ singleton.hpp \ + threadsafequeue.hpp \ utils_global.h \ utils.h