diff --git a/examples/player/mainwindow.cpp b/examples/player/mainwindow.cpp index f7b98cc..ab20793 100644 --- a/examples/player/mainwindow.cpp +++ b/examples/player/mainwindow.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -82,22 +84,12 @@ class MainWindow::MainWindowPrivate void initShortcut() { new QShortcut(QKeySequence::MoveToNextChar, q_ptr, q_ptr, [this] { - qint64 value = controlWidget->position(); - value += 5; - if (value > controlWidget->duration()) { - value = controlWidget->duration(); - } - playerPtr->seek(value * AV_TIME_BASE); - setTitleWidgetText(tr("Fast forward: 5 seconds")); + Ffmpeg::EventPtr eventPtr(new Ffmpeg::SeekRelativeEvent(5)); + playerPtr->addEvent(eventPtr); }); new QShortcut(QKeySequence::MoveToPreviousChar, q_ptr, q_ptr, [this] { - qint64 value = controlWidget->position(); - value -= 5; - if (value < 0) { - value = 0; - } - playerPtr->seek(value * AV_TIME_BASE); - setTitleWidgetText(tr("Fast return: 5 seconds")); + Ffmpeg::EventPtr eventPtr(new Ffmpeg::SeekRelativeEvent(-5)); + playerPtr->addEvent(eventPtr); }); new QShortcut(QKeySequence::MoveToPreviousLine, q_ptr, q_ptr, [this] { controlWidget->setVolume(controlWidget->volume() + 5); @@ -186,7 +178,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , d_ptr(new MainWindowPrivate(this)) { - Ffmpeg::Utils::printFfmpegInfo(); + Ffmpeg::printFfmpegInfo(); setupUI(); buildConnect(); @@ -323,19 +315,19 @@ void MainWindow::jump(const QModelIndex &index) void MainWindow::onProcessEvents() { - while (d_ptr->playerPtr->eventCount() > 0) { - auto eventPtr = d_ptr->playerPtr->takeEvent(); + while (d_ptr->playerPtr->propertyChangeEventSize() > 0) { + auto eventPtr = d_ptr->playerPtr->takePropertyChangeEvent(); switch (eventPtr->type()) { - case Ffmpeg::Event::EventType::DurationChanged: { - auto durationEvent = static_cast(eventPtr.data()); + case Ffmpeg::PropertyChangeEvent::EventType::Duration: { + auto durationEvent = static_cast(eventPtr.data()); d_ptr->controlWidget->setDuration(durationEvent->duration() / AV_TIME_BASE); } break; - case Ffmpeg::Event::EventType::PositionChanged: { - auto positionEvent = static_cast(eventPtr.data()); + case Ffmpeg::PropertyChangeEvent::EventType::Position: { + auto positionEvent = static_cast(eventPtr.data()); d_ptr->controlWidget->setPosition(positionEvent->position() / AV_TIME_BASE); } break; - case Ffmpeg::Event::EventType::MediaStateChanged: { - auto stateEvent = static_cast(eventPtr.data()); + case Ffmpeg::PropertyChangeEvent::EventType::MediaState: { + auto stateEvent = static_cast(eventPtr.data()); switch (stateEvent->state()) { case Ffmpeg::MediaState::Stopped: d_ptr->controlWidget->setPlayButtonChecked(false); @@ -354,16 +346,16 @@ void MainWindow::onProcessEvents() default: break; } } break; - case Ffmpeg::Event::EventType::CacheSpeedChanged: { - auto speedEvent = static_cast(eventPtr.data()); + case Ffmpeg::PropertyChangeEvent::EventType::CacheSpeed: { + auto speedEvent = static_cast(eventPtr.data()); d_ptr->controlWidget->setCacheSpeed(speedEvent->speed()); } break; - case Ffmpeg::Event::MediaTracksChanged: { + case Ffmpeg::PropertyChangeEvent::MediaTrack: { qDeleteAll(d_ptr->audioTracksGroup->actions()); qDeleteAll(d_ptr->videoTracksGroup->actions()); qDeleteAll(d_ptr->subTracksGroup->actions()); - auto tracksEvent = static_cast(eventPtr.data()); + auto tracksEvent = static_cast(eventPtr.data()); auto tracks = tracksEvent->tracks(); for (const auto &track : qAsConst(tracks)) { std::unique_ptr actionPtr(new QAction(track.info(), this)); @@ -391,7 +383,18 @@ void MainWindow::onProcessEvents() } } } break; - case Ffmpeg::Event::Error: { + case Ffmpeg::PropertyChangeEvent::SeekChanged: { + auto seekEvent = static_cast(eventPtr.data()); + int value = seekEvent->position() * 100 / d_ptr->playerPtr->duration(); + auto text = tr("Seeked To %1 (key frame) / %2 (%3%)") + .arg(QTime::fromMSecsSinceStartOfDay(seekEvent->position() / 1000) + .toString("hh:mm:ss"), + QTime::fromMSecsSinceStartOfDay(d_ptr->playerPtr->duration() / 1000) + .toString("hh:mm:ss"), + QString::number(value)); + d_ptr->setTitleWidgetText(text); + } break; + case Ffmpeg::PropertyChangeEvent::Error: { auto errorEvent = static_cast(eventPtr.data()); const auto text = tr("Error[%1]:%2.") .arg(QString::number(errorEvent->error().errorCode()), @@ -428,12 +431,6 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event) auto e = static_cast(event); d_ptr->menu->exec(e->globalPos()); } break; - // case QEvent::MouseButtonPress: { - // auto e = static_cast(event); - // if (e->button() & Qt::LeftButton) { - // d_ptr->mpvPlayer->pause(); - // } - // } break; case QEvent::MouseButtonDblClick: if (isFullScreen()) { showNormal(); @@ -518,16 +515,15 @@ void MainWindow::buildConnect() connect(d_ptr->controlWidget, &ControlWidget::leavePosition, this, &MainWindow::onLeaveSlider); connect(d_ptr->controlWidget, &ControlWidget::seek, d_ptr->playerPtr.data(), [this](int value) { qint64 position = value; - d_ptr->playerPtr->seek(position * AV_TIME_BASE); - d_ptr->setTitleWidgetText( - tr("Fast forward to: %1") - .arg(QTime::fromMSecsSinceStartOfDay(value * 1000).toString("hh:mm:ss"))); + Ffmpeg::EventPtr eventPtr(new Ffmpeg::SeekEvent(position * AV_TIME_BASE)); + d_ptr->playerPtr->addEvent(eventPtr); }); connect(d_ptr->controlWidget, &ControlWidget::play, this, [this](bool checked) { if (checked && !d_ptr->playerPtr->isRunning()) d_ptr->playerPtr->onPlay(); else { - d_ptr->playerPtr->pause(!checked); + Ffmpeg::EventPtr eventPtr(new Ffmpeg::PauseEvent(!checked)); + d_ptr->playerPtr->addEvent(eventPtr); } }); connect(d_ptr->controlWidget, diff --git a/examples/transcoder/mainwindow.cc b/examples/transcoder/mainwindow.cc index 6b67ffa..a230540 100644 --- a/examples/transcoder/mainwindow.cc +++ b/examples/transcoder/mainwindow.cc @@ -24,7 +24,7 @@ class MainWindow::MainWindowPrivate audioCodecCbx->setView(new QListView(audioCodecCbx)); audioCodecCbx->setMaxVisibleItems(10); audioCodecCbx->setStyleSheet("QComboBox {combobox-popup:0;}"); - audioCodecCbx->addItems(Ffmpeg::Utils::getCurrentSupportCodecs(AVMEDIA_TYPE_AUDIO, true)); + audioCodecCbx->addItems(Ffmpeg::getCurrentSupportCodecs(AVMEDIA_TYPE_AUDIO, true)); audioCodecCbx->setCurrentIndex(audioCodecCbx->findData(AV_CODEC_ID_AAC)); audioCodecCbx->setCurrentText(avcodec_get_name(AV_CODEC_ID_AAC)); @@ -32,7 +32,7 @@ class MainWindow::MainWindowPrivate videoCodecCbx->setView(new QListView(videoCodecCbx)); videoCodecCbx->setMaxVisibleItems(10); videoCodecCbx->setStyleSheet("QComboBox {combobox-popup:0;}"); - videoCodecCbx->addItems(Ffmpeg::Utils::getCurrentSupportCodecs(AVMEDIA_TYPE_VIDEO, true)); + videoCodecCbx->addItems(Ffmpeg::getCurrentSupportCodecs(AVMEDIA_TYPE_VIDEO, true)); videoCodecCbx->setCurrentText(avcodec_get_name(AV_CODEC_ID_H264)); quailtySbx = new QSpinBox(q_ptr); @@ -117,7 +117,7 @@ class MainWindow::MainWindowPrivate { bool audioSet = false; bool videoSet = false; - auto codecs = Ffmpeg::Utils::getFileCodecInfo(filePath); + auto codecs = Ffmpeg::getFileCodecInfo(filePath); for (const auto &codec : qAsConst(codecs)) { if (audioSet && videoSet) { break; @@ -196,7 +196,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , d_ptr(new MainWindowPrivate(this)) { - Ffmpeg::Utils::printFfmpegInfo(); + Ffmpeg::printFfmpegInfo(); setupUI(); buildConnect(); @@ -214,15 +214,15 @@ void MainWindow::onError(const Ffmpeg::AVError &avError) void MainWindow::onVideoEncoderChanged() { - auto quantizer = Ffmpeg::Utils::getCodecQuantizer(d_ptr->videoCodecCbx->currentText()); + auto quantizer = Ffmpeg::getCodecQuantizer(d_ptr->videoCodecCbx->currentText()); d_ptr->quailtySbx->setRange(quantizer.first, quantizer.second); } void MainWindow::onOpenInputFile() { - const QString path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) - .value(0, QDir::homePath()); - const QString filePath + const auto path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) + .value(0, QDir::homePath()); + const auto filePath = QFileDialog::getOpenFileName(this, tr("Open File"), path, diff --git a/ffmpeg/audiodecoder.cpp b/ffmpeg/audiodecoder.cpp index cbfaf63..956dfde 100644 --- a/ffmpeg/audiodecoder.cpp +++ b/ffmpeg/audiodecoder.cpp @@ -1,6 +1,9 @@ #include "audiodecoder.h" #include "avcontextinfo.h" #include "decoderaudioframe.h" +#include "ffmpegutils.hpp" + +#include #include @@ -15,6 +18,23 @@ class AudioDecoder::AudioDecoderPrivate decoderAudioFrame = new DecoderAudioFrame(q_ptr); } + void processEvent() + { + while (q_ptr->m_runing.load() && q_ptr->m_eventQueue.size() > 0) { + auto eventPtr = q_ptr->m_eventQueue.take(); + switch (eventPtr->type()) { + case Event::EventType::Pause: decoderAudioFrame->addEvent(eventPtr); break; + case Event::EventType::Seek: { + decoderAudioFrame->clear(); + decoderAudioFrame->addEvent(eventPtr); + auto seekEvent = static_cast(eventPtr.data()); + seekEvent->countDown(); + } break; + default: break; + } + } + } + AudioDecoder *q_ptr; DecoderAudioFrame *decoderAudioFrame; @@ -30,7 +50,10 @@ AudioDecoder::AudioDecoder(QObject *parent) &AudioDecoder::positionChanged); } -AudioDecoder::~AudioDecoder() = default; +AudioDecoder::~AudioDecoder() +{ + stopDecoder(); +} void AudioDecoder::setVolume(qreal volume) { @@ -47,18 +70,20 @@ void AudioDecoder::runDecoder() d_ptr->decoderAudioFrame->startDecoder(m_formatContext, m_contextInfo); while (m_runing) { + d_ptr->processEvent(); + auto packetPtr(m_queue.take()); if (packetPtr.isNull()) { continue; } auto framePtrs = m_contextInfo->decodeFrame(packetPtr); for (const auto &framePtr : framePtrs) { - Ffmpeg::calculatePts(framePtr.data(), m_contextInfo, m_formatContext); + calculatePts(framePtr.data(), m_contextInfo, m_formatContext); d_ptr->decoderAudioFrame->append(framePtr); } } while (m_runing && d_ptr->decoderAudioFrame->size() != 0) { - msleep(Sleep_Queue_Full_Milliseconds); + msleep(s_waitQueueEmptyMilliseconds); } d_ptr->decoderAudioFrame->stopDecoder(); diff --git a/ffmpeg/clock.cc b/ffmpeg/clock.cc index d1c7441..1527f25 100644 --- a/ffmpeg/clock.cc +++ b/ffmpeg/clock.cc @@ -107,7 +107,7 @@ 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(); + d_ptr->last_updated = 0; } } diff --git a/ffmpeg/decoder.cc b/ffmpeg/decoder.cc index de4f2ea..e27d5f6 100644 --- a/ffmpeg/decoder.cc +++ b/ffmpeg/decoder.cc @@ -1,41 +1 @@ #include "decoder.h" -#include "frame.hpp" -#include "packet.h" - -extern "C" { -#include -} - -namespace Ffmpeg { - -void calculatePts(Frame *frame, AVContextInfo *contextInfo, FormatContext *formatContext) -{ - auto tb = contextInfo->stream()->time_base; - auto frame_rate = formatContext->guessFrameRate(contextInfo->stream()); - // 当前帧播放时长 - auto duration = (frame_rate.num && frame_rate.den - ? av_q2d(AVRational{frame_rate.den, frame_rate.num}) - : 0); - // 当前帧显示时间戳 - auto *avFrame = frame->avFrame(); - auto pts = (avFrame->pts == AV_NOPTS_VALUE) ? NAN : avFrame->pts * av_q2d(tb); - frame->setDuration(duration * AV_TIME_BASE); - frame->setPts(pts * AV_TIME_BASE); - // qDebug() << "Frame duration:" << duration << "pts:" << pts << "tb:" << tb.num << tb.den - // << "frame_rate:" << frame_rate.num << frame_rate.den; -} - -void calculatePts(Packet *packet, AVContextInfo *contextInfo) -{ - auto tb = contextInfo->stream()->time_base; - // 当前帧播放时长 - auto *avPacket = packet->avPacket(); - auto duration = avPacket->duration * av_q2d(tb); - // 当前帧显示时间戳 - auto pts = (avPacket->pts == AV_NOPTS_VALUE) ? NAN : avPacket->pts * av_q2d(tb); - packet->setDuration(duration * AV_TIME_BASE); - packet->setPts(pts * AV_TIME_BASE); - // qDebug() << "Packet duration:" << duration << "pts:" << pts << "tb:" << tb.num << tb.den; -} - -} // namespace Ffmpeg diff --git a/ffmpeg/decoder.h b/ffmpeg/decoder.h index 610b39f..299a9e1 100644 --- a/ffmpeg/decoder.h +++ b/ffmpeg/decoder.h @@ -4,16 +4,16 @@ #include #include +#include #include +#include #include "avcontextinfo.h" -#include "codeccontext.h" #include "formatcontext.h" -#define Sleep_Queue_Full_Milliseconds 50 - namespace Ffmpeg { +static const auto s_waitQueueEmptyMilliseconds = 50; static const auto s_frameQueueSize = 25; template @@ -24,7 +24,7 @@ class Decoder : public QThread : QThread(parent) , m_queue(s_frameQueueSize) {} - ~Decoder() override { stopDecoder(); } + ~Decoder() override = default; void startDecoder(FormatContext *formatContext, AVContextInfo *contextInfo) { @@ -53,7 +53,18 @@ class Decoder : public QThread void clear() { m_queue.clear(); } - void wakeup() { m_queue.put(T()); } + void wakeup() + { + if (m_queue.empty()) { + m_queue.putHead(T()); + } + } + + void addEvent(const EventPtr &event) + { + m_eventQueue.put(event); + wakeup(); + } protected: virtual void runDecoder() = 0; @@ -74,14 +85,12 @@ class Decoder : public QThread } Utils::BoundedBlockingQueue m_queue; + Utils::ThreadSafeQueue m_eventQueue; AVContextInfo *m_contextInfo = nullptr; FormatContext *m_formatContext = nullptr; std::atomic_bool m_runing = true; }; -void calculatePts(Frame *frame, AVContextInfo *contextInfo, FormatContext *formatContext); -void calculatePts(Packet *packet, AVContextInfo *contextInfo); - } // namespace Ffmpeg #endif // DECODER_H diff --git a/ffmpeg/decoderaudioframe.cpp b/ffmpeg/decoderaudioframe.cpp index f253b20..c1fc409 100644 --- a/ffmpeg/decoderaudioframe.cpp +++ b/ffmpeg/decoderaudioframe.cpp @@ -3,6 +3,9 @@ #include "clock.hpp" #include "codeccontext.h" +#include +#include + #include #include #include @@ -25,8 +28,35 @@ class DecoderAudioFrame::DecoderAudioFramePrivate { clock = new Clock(q); } + ~DecoderAudioFramePrivate() = default; + void processEvent(bool &firstFrame) + { + while (q_ptr->m_runing.load() && q_ptr->m_eventQueue.size() > 0) { + qDebug() << "AudioFramePrivate::processEvent"; + auto eventPtr = q_ptr->m_eventQueue.take(); + switch (eventPtr->type()) { + case Event::EventType::Pause: { + auto pauseEvent = static_cast(eventPtr.data()); + auto paused = pauseEvent->paused(); + clock->setPaused(paused); + if (paused) { + firstFrame = false; + } + } break; + case Event::EventType::Seek: { + auto seekEvent = static_cast(eventPtr.data()); + auto position = seekEvent->position(); + q_ptr->clear(); + clock->reset(position); + firstFrame = false; + } + default: break; + } + } + } + DecoderAudioFrame *q_ptr; QScopedPointer audioSinkPtr; @@ -48,7 +78,10 @@ DecoderAudioFrame::DecoderAudioFrame(QObject *parent) buildConnect(); } -DecoderAudioFrame::~DecoderAudioFrame() = default; +DecoderAudioFrame::~DecoderAudioFrame() +{ + stopDecoder(); +} void DecoderAudioFrame::setVolume(qreal volume) { @@ -72,17 +105,6 @@ void DecoderAudioFrame::onStateChanged(QAudio::State state) if (d_ptr->audioSinkPtr->error() != QAudio::NoError) { qWarning() << tr("QAudioSink Error:") << state << d_ptr->audioSinkPtr->error(); } - // switch (state) { - // case QAudio::StoppedState: - // // Stopped for other reasons - // if (d_ptr->audioSink->error() != QAudio::NoError) { - // qWarning() << tr("QAudioSink Error:") << d_ptr->audioSink->error(); - // } - // break; - // default: - // // ... other cases as appropriate - // break; - // } } void DecoderAudioFrame::onAudioOutputsChanged() @@ -98,24 +120,30 @@ void DecoderAudioFrame::runDecoder() AudioFrameConverter audioConverter(m_contextInfo->codecCtx(), format); bool firstFrame = false; while (m_runing.load()) { + d_ptr->processEvent(firstFrame); + checkDefaultAudioOutput(audioDevice); auto framePtr(m_queue.take()); if (framePtr.isNull()) { continue; } else if (!firstFrame) { + qDebug() << "Audio firstFrame: " + << QTime::fromMSecsSinceStartOfDay(framePtr->pts() / 1000) + .toString("hh:mm:ss.zzz"); firstFrame = true; d_ptr->clock->reset(framePtr->pts()); } 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; } + auto emitPosition = qScopeGuard([=]() { emit positionChanged(pts); }); if (!Clock::adjustDelay(delay)) { + qDebug() << "Audio Delay: " << delay; dropNum++; continue; } diff --git a/ffmpeg/decodersubtitleframe.cc b/ffmpeg/decodersubtitleframe.cc index 56ead5c..744e38e 100644 --- a/ffmpeg/decodersubtitleframe.cc +++ b/ffmpeg/decodersubtitleframe.cc @@ -2,6 +2,8 @@ #include "clock.hpp" #include "codeccontext.h" +#include +#include #include #include @@ -26,6 +28,33 @@ class DecoderSubtitleFrame::DecoderSubtitleFramePrivate clock = new Clock(q); } + void processEvent(Ass *ass, bool &firstFrame) + { + while (q_ptr->m_runing.load() && q_ptr->m_eventQueue.size() > 0) { + qDebug() << "DecoderSubtitleFrame::processEvent"; + auto eventPtr = q_ptr->m_eventQueue.take(); + switch (eventPtr->type()) { + case Event::EventType::Pause: { + auto pauseEvent = static_cast(eventPtr.data()); + auto paused = pauseEvent->paused(); + clock->setPaused(paused); + if (paused) { + firstFrame = false; + } + } break; + case Event::EventType::Seek: { + auto seekEvent = static_cast(eventPtr.data()); + auto position = seekEvent->position(); + q_ptr->clear(); + clock->reset(position); + ass->flushASSEvents(); + firstFrame = false; + } + default: break; + } + } + } + DecoderSubtitleFrame *q_ptr; Clock *clock; @@ -43,7 +72,10 @@ DecoderSubtitleFrame::DecoderSubtitleFrame(QObject *parent) , d_ptr(new DecoderSubtitleFramePrivate(this)) {} -DecoderSubtitleFrame::~DecoderSubtitleFrame() = default; +DecoderSubtitleFrame::~DecoderSubtitleFrame() +{ + stopDecoder(); +} void DecoderSubtitleFrame::setVideoResolutionRatio(const QSize &size) { @@ -71,10 +103,15 @@ void DecoderSubtitleFrame::runDecoder() SwsContext *swsContext = nullptr; bool firstFrame = false; while (m_runing.load()) { + d_ptr->processEvent(assPtr.data(), firstFrame); + auto subtitlePtr(m_queue.take()); if (subtitlePtr.isNull()) { continue; } else if (!firstFrame) { + qDebug() << "Subtitle firstFrame: " + << QTime::fromMSecsSinceStartOfDay(subtitlePtr->pts() / 1000) + .toString("hh:mm:ss.zzz"); firstFrame = true; d_ptr->clock->reset(subtitlePtr->pts()); } @@ -91,6 +128,7 @@ void DecoderSubtitleFrame::runDecoder() } auto delayDuration = delay + subtitlePtr->duration(); if (!Clock::adjustDelay(delayDuration)) { + qDebug() << "Subtitle Delay: " << delay; dropNum++; continue; } diff --git a/ffmpeg/decodervideoframe.cpp b/ffmpeg/decodervideoframe.cpp index 72d8aea..c55f8d5 100644 --- a/ffmpeg/decodervideoframe.cpp +++ b/ffmpeg/decodervideoframe.cpp @@ -1,6 +1,8 @@ #include "decodervideoframe.h" #include "clock.hpp" +#include +#include #include #include @@ -22,6 +24,32 @@ class DecoderVideoFrame::DecoderVideoFramePrivate clock = new Clock(q); } + void processEvent(bool &firstFrame) + { + while (q_ptr->m_runing.load() && q_ptr->m_eventQueue.size() > 0) { + qDebug() << "DecoderVideoFrame::processEvent"; + auto eventPtr = q_ptr->m_eventQueue.take(); + switch (eventPtr->type()) { + case Event::EventType::Pause: { + auto pauseEvent = static_cast(eventPtr.data()); + auto paused = pauseEvent->paused(); + clock->setPaused(paused); + if (paused) { + firstFrame = false; + } + } break; + case Event::EventType::Seek: { + auto seekEvent = static_cast(eventPtr.data()); + auto position = seekEvent->position(); + q_ptr->clear(); + clock->reset(position); + firstFrame = false; + } + default: break; + } + } + } + DecoderVideoFrame *q_ptr; Clock *clock; @@ -38,7 +66,10 @@ DecoderVideoFrame::DecoderVideoFrame(QObject *parent) , d_ptr(new DecoderVideoFramePrivate(this)) {} -DecoderVideoFrame::~DecoderVideoFrame() = default; +DecoderVideoFrame::~DecoderVideoFrame() +{ + stopDecoder(); +} void DecoderVideoFrame::setVideoRenders(QVector videoRenders) { @@ -59,21 +90,27 @@ void DecoderVideoFrame::runDecoder() quint64 dropNum = 0; bool firstFrame = false; while (m_runing.load()) { + d_ptr->processEvent(firstFrame); + auto framePtr(m_queue.take()); if (framePtr.isNull()) { continue; } else if (!firstFrame) { + qDebug() << "Video firstFrame: " + << QTime::fromMSecsSinceStartOfDay(framePtr->pts() / 1000) + .toString("hh:mm:ss.zzz"); firstFrame = true; d_ptr->clock->reset(framePtr->pts()); } 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; } + auto emitPosition = qScopeGuard([=]() { emit positionChanged(pts); }); if (!Clock::adjustDelay(delay)) { + qDebug() << "Video Delay: " << delay; dropNum++; continue; } diff --git a/ffmpeg/event/errorevent.hpp b/ffmpeg/event/errorevent.hpp index 569bb53..87dd008 100644 --- a/ffmpeg/event/errorevent.hpp +++ b/ffmpeg/event/errorevent.hpp @@ -6,11 +6,11 @@ namespace Ffmpeg { -class FFMPEG_EXPORT ErrorEvent : public Event +class FFMPEG_EXPORT ErrorEvent : public PropertyChangeEvent { public: explicit ErrorEvent(const AVError &error, QObject *parent = nullptr) - : Event(parent) + : PropertyChangeEvent(parent) , m_error(error) {} diff --git a/ffmpeg/event/event.hpp b/ffmpeg/event/event.hpp index 3957649..f0e0f41 100644 --- a/ffmpeg/event/event.hpp +++ b/ffmpeg/event/event.hpp @@ -6,27 +6,44 @@ namespace Ffmpeg { -class FFMPEG_EXPORT Event : public QObject +class FFMPEG_EXPORT PropertyChangeEvent : public QObject { Q_OBJECT public: enum EventType { None, - MediaTracksChanged, - DurationChanged, - PositionChanged, - MediaStateChanged, - CacheSpeedChanged, - Error, - - Pause = 100, - Seek, - seekRelative, + Duration, + Position, + MediaTrack, + MediaState, + CacheSpeed, + SeekChanged, + Error }; Q_ENUM(EventType); using QObject::QObject; + auto operator==(const PropertyChangeEvent &other) const -> bool + { + return type() == other.type(); + } + auto operator!=(const PropertyChangeEvent &other) const -> bool { return !(*this == other); } + + [[nodiscard]] virtual auto type() const -> EventType { return None; } +}; + +using PropertyChangeEventPtr = QSharedPointer; + +class FFMPEG_EXPORT Event : public QObject +{ + Q_OBJECT +public: + enum EventType { None, Pause, Seek, seekRelative, GpuDecode }; + Q_ENUM(EventType); + + using QObject::QObject; + auto operator==(const Event &other) const -> bool { return type() == other.type(); } auto operator!=(const Event &other) const -> bool { return !(*this == other); } diff --git a/ffmpeg/event/pauseevent.hpp b/ffmpeg/event/pauseevent.hpp index 020fa30..3c91a73 100644 --- a/ffmpeg/event/pauseevent.hpp +++ b/ffmpeg/event/pauseevent.hpp @@ -4,7 +4,7 @@ namespace Ffmpeg { -class FFMPEG_EXPORT PauseEvent : public Event +class PauseEvent : public Event { public: explicit PauseEvent(bool paused, QObject *parent = nullptr) diff --git a/ffmpeg/event/seekevent.hpp b/ffmpeg/event/seekevent.hpp index 5220228..243cd16 100644 --- a/ffmpeg/event/seekevent.hpp +++ b/ffmpeg/event/seekevent.hpp @@ -2,6 +2,8 @@ #include "event.hpp" +#include + namespace Ffmpeg { class FFMPEG_EXPORT SeekEvent : public Event @@ -10,6 +12,7 @@ class FFMPEG_EXPORT SeekEvent : public Event explicit SeekEvent(qint64 position, QObject *parent = nullptr) : Event(parent) , m_position(position) + , m_latch(0) {} [[nodiscard]] auto type() const -> EventType override { return EventType::Seek; } @@ -17,6 +20,46 @@ class FFMPEG_EXPORT SeekEvent : public Event void setPosition(qint64 position) { m_position = position; } [[nodiscard]] auto position() const -> qint64 { return m_position; } + void setWaitCountdown(qint64 count) { m_latch.setCount(count); } + void countDown() { m_latch.countDown(); } + void wait() { m_latch.wait(); } + +private: + qint64 m_position = 0; + Utils::CountDownLatch m_latch; +}; + +class FFMPEG_EXPORT SeekRelativeEvent : public Event +{ +public: + explicit SeekRelativeEvent(qint64 relativePosition, QObject *parent = nullptr) + : Event(parent) + , m_relativePosition(relativePosition) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::seekRelative; } + + // second + void setRelativePosition(qint64 relativePosition) { m_relativePosition = relativePosition; } + [[nodiscard]] auto relativePosition() const -> qint64 { return m_relativePosition; } + +private: + qint64 m_relativePosition = 0; +}; + +class FFMPEG_EXPORT SeekChangedEvent : public PropertyChangeEvent +{ +public: + explicit SeekChangedEvent(qint64 position, QObject *parent = nullptr) + : PropertyChangeEvent(parent) + , m_position(position) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::SeekChanged; } + + void setPosition(qint64 position) { m_position = position; } + [[nodiscard]] auto position() const -> qint64 { return m_position; } + private: qint64 m_position = 0; }; diff --git a/ffmpeg/event/trackevent.hpp b/ffmpeg/event/trackevent.hpp index 5cc6bcb..55e877a 100644 --- a/ffmpeg/event/trackevent.hpp +++ b/ffmpeg/event/trackevent.hpp @@ -6,15 +6,15 @@ namespace Ffmpeg { -class FFMPEG_EXPORT TracksChangedEvent : public Event +class FFMPEG_EXPORT MediaTrackEvent : public PropertyChangeEvent { public: - explicit TracksChangedEvent(const QVector &tracks, QObject *parent = nullptr) - : Event(parent) + explicit MediaTrackEvent(const QVector &tracks, QObject *parent = nullptr) + : PropertyChangeEvent(parent) , m_tracks(tracks) {} - - [[nodiscard]] auto type() const -> EventType override { return MediaTracksChanged; } + + [[nodiscard]] auto type() const -> EventType override { return MediaTrack; } void setTracks(const QVector &tracks) { m_tracks = tracks; } [[nodiscard]] auto tracks() const -> QVector { return m_tracks; } diff --git a/ffmpeg/event/valueevent.hpp b/ffmpeg/event/valueevent.hpp index f1f4a0c..dfebcb0 100644 --- a/ffmpeg/event/valueevent.hpp +++ b/ffmpeg/event/valueevent.hpp @@ -6,15 +6,15 @@ namespace Ffmpeg { -class FFMPEG_EXPORT DurationChangedEvent : public Event +class FFMPEG_EXPORT DurationEvent : public PropertyChangeEvent { public: - explicit DurationChangedEvent(qint64 duration, QObject *parent = nullptr) - : Event(parent) + explicit DurationEvent(qint64 duration, QObject *parent = nullptr) + : PropertyChangeEvent(parent) , m_duration(duration) {} - [[nodiscard]] auto type() const -> EventType override { return EventType::DurationChanged; } + [[nodiscard]] auto type() const -> EventType override { return EventType::Duration; } void setDuration(qint64 duration) { m_duration = duration; } [[nodiscard]] auto duration() const -> qint64 { return m_duration; } @@ -23,15 +23,15 @@ class FFMPEG_EXPORT DurationChangedEvent : public Event qint64 m_duration = 0; }; -class FFMPEG_EXPORT PositionChangedEvent : public Event +class FFMPEG_EXPORT PositionEvent : public PropertyChangeEvent { public: - explicit PositionChangedEvent(qint64 position, QObject *parent = nullptr) - : Event(parent) + explicit PositionEvent(qint64 position, QObject *parent = nullptr) + : PropertyChangeEvent(parent) , m_position(position) {} - [[nodiscard]] auto type() const -> EventType override { return EventType::PositionChanged; } + [[nodiscard]] auto type() const -> EventType override { return EventType::Position; } void setPosition(qint64 position) { m_position = position; } [[nodiscard]] auto position() const -> qint64 { return m_position; } @@ -40,32 +40,32 @@ class FFMPEG_EXPORT PositionChangedEvent : public Event qint64 m_position = 0; }; -class FFMPEG_EXPORT MediaStateChangedEvent : public Event +class FFMPEG_EXPORT MediaStateEvent : public PropertyChangeEvent { public: - explicit MediaStateChangedEvent(MediaState state, QObject *parent = nullptr) - : Event(parent) + explicit MediaStateEvent(Ffmpeg::MediaState state, QObject *parent = nullptr) + : PropertyChangeEvent(parent) , m_state(state) {} - [[nodiscard]] auto type() const -> EventType override { return EventType::MediaStateChanged; } + [[nodiscard]] auto type() const -> EventType override { return EventType::MediaState; } - void setState(MediaState state) { m_state = state; } - [[nodiscard]] auto state() const -> MediaState { return m_state; } + void setState(Ffmpeg::MediaState state) { m_state = state; } + [[nodiscard]] auto state() const -> Ffmpeg::MediaState { return m_state; } private: - MediaState m_state = MediaState::Stopped; + Ffmpeg::MediaState m_state = Ffmpeg::MediaState::Stopped; }; -class FFMPEG_EXPORT CacheSpeedChangedEvent : public Event +class FFMPEG_EXPORT CacheSpeedEvent : public PropertyChangeEvent { public: - explicit CacheSpeedChangedEvent(qint64 speed, QObject *parent = nullptr) - : Event(parent) + explicit CacheSpeedEvent(qint64 speed, QObject *parent = nullptr) + : PropertyChangeEvent(parent) , m_speed(speed) {} - [[nodiscard]] auto type() const -> EventType override { return EventType::CacheSpeedChanged; } + [[nodiscard]] auto type() const -> EventType override { return EventType::CacheSpeed; } void setSpeed(qint64 speed) { m_speed = speed; } [[nodiscard]] auto speed() const -> qint64 { return m_speed; } diff --git a/ffmpeg/ffmpegutils.cc b/ffmpeg/ffmpegutils.cc index 3359e7c..8527cea 100644 --- a/ffmpeg/ffmpegutils.cc +++ b/ffmpeg/ffmpegutils.cc @@ -2,6 +2,8 @@ #include "avcontextinfo.h" #include "codeccontext.h" #include "formatcontext.h" +#include "frame.hpp" +#include "packet.h" #include @@ -15,7 +17,35 @@ extern "C" { namespace Ffmpeg { -namespace Utils { +void calculatePts(Frame *frame, AVContextInfo *contextInfo, FormatContext *formatContext) +{ + auto tb = contextInfo->stream()->time_base; + auto frame_rate = formatContext->guessFrameRate(contextInfo->stream()); + // 当前帧播放时长 + auto duration = (frame_rate.num && frame_rate.den + ? av_q2d(AVRational{frame_rate.den, frame_rate.num}) + : 0); + // 当前帧显示时间戳 + auto *avFrame = frame->avFrame(); + auto pts = (avFrame->pts == AV_NOPTS_VALUE) ? NAN : avFrame->pts * av_q2d(tb); + frame->setDuration(duration * AV_TIME_BASE); + frame->setPts(pts * AV_TIME_BASE); + // qDebug() << "Frame duration:" << duration << "pts:" << pts << "tb:" << tb.num << tb.den + // << "frame_rate:" << frame_rate.num << frame_rate.den; +} + +void calculatePts(Packet *packet, AVContextInfo *contextInfo) +{ + auto tb = contextInfo->stream()->time_base; + // 当前帧播放时长 + auto *avPacket = packet->avPacket(); + auto duration = avPacket->duration * av_q2d(tb); + // 当前帧显示时间戳 + auto pts = (avPacket->pts == AV_NOPTS_VALUE) ? NAN : avPacket->pts * av_q2d(tb); + packet->setDuration(duration * AV_TIME_BASE); + packet->setPts(pts * AV_TIME_BASE); + // qDebug() << "Packet duration:" << duration << "pts:" << pts << "tb:" << tb.num << tb.den; +} int compare_codec_desc(const void *a, const void *b) { @@ -180,6 +210,4 @@ QStringList getCurrentSupportCodecs(AVMediaType mediaType, bool encoder) return codecnames; } -} // namespace Utils - } // namespace Ffmpeg diff --git a/ffmpeg/ffmpegutils.hpp b/ffmpeg/ffmpegutils.hpp index 6056d8d..1ebb8e8 100644 --- a/ffmpeg/ffmpegutils.hpp +++ b/ffmpeg/ffmpegutils.hpp @@ -14,10 +14,16 @@ struct AVCodec; namespace Ffmpeg { -namespace Utils { +class Packet; +class Frame; +class AVContextInfo; +class FormatContext; void FFMPEG_EXPORT printFfmpegInfo(); +void calculatePts(Frame *frame, AVContextInfo *contextInfo, FormatContext *formatContext); +void calculatePts(Packet *packet, AVContextInfo *contextInfo); + QVector getCurrentHWDeviceTypes(); auto getPixelFormat(const AVCodec *codec, AVHWDeviceType type) -> AVPixelFormat; @@ -36,8 +42,6 @@ QPair FFMPEG_EXPORT getCodecQuantizer(const QString &codecname); auto FFMPEG_EXPORT getCurrentSupportCodecs(AVMediaType mediaType, bool encoder) -> QStringList; -} // namespace Utils - } // namespace Ffmpeg #endif // FFMPEGUTILS_HPP diff --git a/ffmpeg/gpu/hardwaredecode.cc b/ffmpeg/gpu/hardwaredecode.cc index 2766e67..deea793 100644 --- a/ffmpeg/gpu/hardwaredecode.cc +++ b/ffmpeg/gpu/hardwaredecode.cc @@ -42,7 +42,7 @@ class HardWareDecode::HardWareDecodePrivate HardWareDecode *q_ptr; - QVector hwDeviceTypes = Utils::getCurrentHWDeviceTypes(); + QVector hwDeviceTypes = getCurrentHWDeviceTypes(); AVHWDeviceType hwDeviceType = AV_HWDEVICE_TYPE_NONE; BufferRef *bufferRef; bool vaild = false; @@ -64,7 +64,7 @@ bool HardWareDecode::initPixelFormat(const AVCodec *decoder) return false; } for (AVHWDeviceType type : qAsConst(d_ptr->hwDeviceTypes)) { - hw_pix_fmt = Utils::getPixelFormat(decoder, type); + hw_pix_fmt = getPixelFormat(decoder, type); if (hw_pix_fmt != AV_PIX_FMT_NONE) { d_ptr->hwDeviceType = type; break; diff --git a/ffmpeg/gpu/hardwareencode.cc b/ffmpeg/gpu/hardwareencode.cc index 35318a7..2aef3c2 100644 --- a/ffmpeg/gpu/hardwareencode.cc +++ b/ffmpeg/gpu/hardwareencode.cc @@ -27,7 +27,7 @@ class HardWareEncode::HardWareEncodePrivate HardWareEncode *q_ptr; - QVector hwDeviceTypes = Utils::getCurrentHWDeviceTypes(); + QVector hwDeviceTypes = getCurrentHWDeviceTypes(); AVHWDeviceType hwDeviceType = AV_HWDEVICE_TYPE_NONE; BufferRef *bufferRef; AVPixelFormat sw_format = AV_PIX_FMT_NV12; @@ -56,7 +56,7 @@ bool HardWareEncode::initEncoder(const AVCodec *encoder) } auto hw_pix_fmt = AV_PIX_FMT_NONE; for (AVHWDeviceType type : qAsConst(d_ptr->hwDeviceTypes)) { - hw_pix_fmt = Utils::getPixelFormat(encoder, type); + hw_pix_fmt = getPixelFormat(encoder, type); if (hw_pix_fmt != AV_PIX_FMT_NONE) { d_ptr->hwDeviceType = type; break; diff --git a/ffmpeg/player.cpp b/ffmpeg/player.cpp index 80ae577..d62fbaf 100644 --- a/ffmpeg/player.cpp +++ b/ffmpeg/player.cpp @@ -10,6 +10,8 @@ #include "videodecoder.h" #include +#include +#include #include #include #include @@ -44,7 +46,9 @@ class Player::PlayerPrivate QObject::connect(AVErrorManager::instance(), &AVErrorManager::error, q_ptr, - [this](const AVError &error) { addEvent(new ErrorEvent(error)); }); + [this](const AVError &error) { + addPropertyChangeEventEvent(new ErrorEvent(error)); + }); } auto initAvCodec() -> bool @@ -61,7 +65,7 @@ class Player::PlayerPrivate return false; } - addEvent(new DurationChangedEvent(formatCtx->duration())); + addPropertyChangeEventEvent(new DurationEvent(formatCtx->duration())); q_ptr->onPositionChanged(0); if (!setBestMediaIndex()) { @@ -129,7 +133,7 @@ class Player::PlayerPrivate QVector tracks = audioTracks; tracks.append(videoTracks); tracks.append(subtitleTracks); - addEvent(new TracksChangedEvent(tracks)); + addPropertyChangeEventEvent(new MediaTrackEvent(tracks)); qInfo() << audioInfo->index() << videoInfo->index() << subtitleInfo->index(); return true; @@ -173,13 +177,15 @@ class Player::PlayerPrivate timer.start(); while (runing) { + processEvent(); + PacketPtr packetPtr(new Packet); if (!formatCtx->readFrame(packetPtr.data())) { break; } speedPtr->addSize(packetPtr->avPacket()->size); if (timer.elapsed() > 1000) { - addEvent(new CacheSpeedChangedEvent(speedPtr->getSpeed())); + addPropertyChangeEventEvent(new CacheSpeedEvent(speedPtr->getSpeed())); timer.restart(); } @@ -198,7 +204,7 @@ class Player::PlayerPrivate } } while (runing && (videoDecoder->size() > 0 || audioDecoder->size() > 0)) { - msleep(Sleep_Queue_Full_Milliseconds); + msleep(s_waitQueueEmptyMilliseconds); } subtitleDecoder->stopDecoder(); videoDecoder->stopDecoder(); @@ -223,7 +229,7 @@ class Player::PlayerPrivate void setMediaState(MediaState mediaState_) { mediaState = mediaState_; - addEvent(new MediaStateChangedEvent(mediaState)); + addPropertyChangeEventEvent(new MediaStateEvent(mediaState)); } [[nodiscard]] QSize resolutionRatio() const @@ -231,14 +237,125 @@ class Player::PlayerPrivate return videoInfo->isIndexVaild() ? videoInfo->resolutionRatio() : QSize(); } - void addEvent(Event *event) + void addPropertyChangeEventEvent(PropertyChangeEvent *event) + { + PropertyChangeEventPtr eventPtr(event); + propertyChangeEventQueue.put(eventPtr); + while (propertyChangeEventQueue.size() > maxPropertyEventQueueSize.load()) { + propertyChangeEventQueue.take(); + } + emit q_ptr->eventIncrease(); + } + + void addEvent(const EventPtr &eventPtr) { - EventPtr eventPtr(event); eventQueue.put(eventPtr); - while (eventQueue.size() > maxEventCount) { + while (eventQueue.size() > maxEventQueueSize.load()) { eventQueue.take(); } - emit q_ptr->eventIncrease(); + if (!paused.load()) { + return; + } + switch (eventPtr->type()) { + case Event::EventType::Pause: break; + default: { + EventPtr eventPtr(new PauseEvent(true)); + eventQueue.put(eventPtr); + } break; + } + waitCondition.wakeAll(); + } + + void processEvent() + { + while (eventQueue.size() > 0) { + auto eventPtr = eventQueue.take(); + switch (eventPtr->type()) { + case Event::EventType::Pause: processPauseEvent(eventPtr); break; + case Event::EventType::Seek: processSeekEvent(eventPtr); break; + case Event::EventType::seekRelative: processSeekRelativeEvent(eventPtr); break; + default: break; + } + } + } + + void processPauseEvent(const EventPtr &eventPtr) + { + auto pauseEvent = static_cast(eventPtr.data()); + if (pauseEvent->paused()) { + setMediaState(MediaState::Pausing); + } else if (q_ptr->isRunning()) { + setMediaState(isOpen ? MediaState::Playing : MediaState::Opening); + } else { + setMediaState(MediaState::Stopped); + } + audioDecoder->addEvent(eventPtr); + videoDecoder->addEvent(eventPtr); + subtitleDecoder->addEvent(eventPtr); + paused.store(pauseEvent->paused()); + if (paused.load()) { + QMutexLocker locker(&mutex); + waitCondition.wait(&mutex); + } + } + + void processSeekEvent(const EventPtr &eventPtr) + { + qInfo() << "Seek To: " + << QTime::fromMSecsSinceStartOfDay(position / 1000).toString("hh:mm:ss.zzz"); + QElapsedTimer timer; + timer.start(); + q_ptr->blockSignals(true); + Clock::globalSerialRef(); + int count = 0; + if (audioInfo->isIndexVaild()) { + count++; + } + if (videoInfo->isIndexVaild()) { + count++; + } + if (subtitleInfo->isIndexVaild()) { + count++; + } + auto seekEvent = static_cast(eventPtr.data()); + seekEvent->setWaitCountdown(count); + audioDecoder->clear(); + audioDecoder->addEvent(eventPtr); + videoDecoder->clear(); + videoDecoder->addEvent(eventPtr); + subtitleDecoder->clear(); + subtitleDecoder->addEvent(eventPtr); + auto position = seekEvent->position(); + seekEvent->wait(); + + formatCtx->seek(position); + if (audioInfo->isIndexVaild()) { + audioInfo->codecCtx()->flush(); + } + if (videoInfo->isIndexVaild()) { + videoInfo->codecCtx()->flush(); + } + if (subtitleInfo->isIndexVaild()) { + subtitleInfo->codecCtx()->flush(); + } + q_ptr->blockSignals(false); + qInfo() << "Seeked elapsed: " << timer.elapsed(); + + addPropertyChangeEventEvent(new SeekChangedEvent(position)); + } + + void processSeekRelativeEvent(const EventPtr &eventPtr) + { + auto seekRelativeEvent = static_cast(eventPtr.data()); + auto relativePosition = seekRelativeEvent->relativePosition(); + auto position = this->position + relativePosition * AV_TIME_BASE; + if (position < 0) { + position = 0; + } else if (position > q_ptr->duration()) { + position = q_ptr->duration(); + } + EventPtr seekEventPtr(new SeekEvent(position)); + eventQueue.putHead(seekEventPtr); } Player *q_ptr; @@ -259,8 +376,14 @@ class Player::PlayerPrivate qint64 position = 0; std::atomic mediaState = MediaState::Stopped; + std::atomic_bool paused = false; + QMutex mutex; + QWaitCondition waitCondition; + + Utils::ThreadSafeQueue propertyChangeEventQueue; + std::atomic maxPropertyEventQueueSize = 100; Utils::ThreadSafeQueue eventQueue; - int maxEventCount = 100; + std::atomic maxEventQueueSize = 100; QVector videoRenders = {}; }; @@ -309,6 +432,7 @@ void Player::onStop() { buildConnect(false); d_ptr->runing = false; + d_ptr->waitCondition.wakeAll(); if (isRunning()) { quit(); } @@ -328,17 +452,11 @@ void Player::onStop() void Player::onPositionChanged(qint64 position) { auto diff = (position - d_ptr->position) / AV_TIME_BASE; - if (diff < 1) { + if (qAbs(diff) < 1) { return; } d_ptr->position = position; - d_ptr->addEvent(new PositionChangedEvent(position)); -} - -void Player::seek(qint64 position) -{ - qInfo() << "Seek To: " - << QTime::fromMSecsSinceStartOfDay(position / 1000).toString("hh:mm:ss.zzz"); + d_ptr->addPropertyChangeEventEvent(new PositionEvent(position)); } void Player::setAudioTrack(const QString &text) // 停止再播放最简单 之后在优化 @@ -419,17 +537,6 @@ auto Player::speed() -> double return 1.0; } -void Player::pause(bool status) -{ - if (status) { - d_ptr->setMediaState(MediaState::Pausing); - } else if (isRunning()) { - d_ptr->setMediaState(d_ptr->isOpen ? MediaState::Playing : MediaState::Opening); - } else { - d_ptr->setMediaState(MediaState::Stopped); - } -} - void Player::setUseGpuDecode(bool on) { // d_ptr->gpuDecode = on; @@ -518,14 +625,48 @@ QVector Player::videoRenders() return d_ptr->videoRenders; } -size_t Player::eventCount() const +void Player::setPropertyEventQueueMaxSize(size_t size) +{ + d_ptr->maxPropertyEventQueueSize.store(size); +} + +size_t Player::propertEventyQueueMaxSize() const +{ + return d_ptr->maxPropertyEventQueueSize.load(); +} + +size_t Player::propertyChangeEventSize() const +{ + return d_ptr->propertyChangeEventQueue.size(); +} + +PropertyChangeEventPtr Player::takePropertyChangeEvent() +{ + return d_ptr->propertyChangeEventQueue.take(); +} + +void Player::setEventQueueMaxSize(size_t size) +{ + d_ptr->maxEventQueueSize.store(size); +} + +size_t Player::eventQueueMaxSize() const +{ + return d_ptr->maxEventQueueSize.load(); +} + +[[nodiscard]] size_t Player::eventSize() const { return d_ptr->eventQueue.size(); } -EventPtr Player::takeEvent() +bool Player::addEvent(const EventPtr &eventPtr) { - return d_ptr->eventQueue.take(); + if (!isRunning()) { + return false; + } + d_ptr->addEvent(eventPtr); + return true; } void Player::run() diff --git a/ffmpeg/player.h b/ffmpeg/player.h index 8de858b..df493aa 100644 --- a/ffmpeg/player.h +++ b/ffmpeg/player.h @@ -10,8 +10,6 @@ namespace Ffmpeg { -class AVError; -class AVContextInfo; class VideoRender; class FFMPEG_EXPORT Player : public QThread @@ -26,15 +24,11 @@ class FFMPEG_EXPORT Player : public QThread auto isOpen() -> bool; - void seek(qint64 position); // microsecond - void setVolume(qreal volume); void setSpeed(double speed); auto speed() -> double; - void pause(bool status = true); - void setUseGpuDecode(bool on); auto isGpuDecode() -> bool; @@ -56,8 +50,15 @@ class FFMPEG_EXPORT Player : public QThread void setVideoRenders(QVector videoRenders); QVector videoRenders(); - [[nodiscard]] size_t eventCount() const; - EventPtr takeEvent(); + void setPropertyEventQueueMaxSize(size_t size); + [[nodiscard]] size_t propertEventyQueueMaxSize() const; + [[nodiscard]] size_t propertyChangeEventSize() const; + PropertyChangeEventPtr takePropertyChangeEvent(); + + void setEventQueueMaxSize(size_t size); + [[nodiscard]] size_t eventQueueMaxSize() const; + [[nodiscard]] size_t eventSize() const; + bool addEvent(const EventPtr &eventPtr); public slots: void onPlay(); diff --git a/ffmpeg/subtitledecoder.cpp b/ffmpeg/subtitledecoder.cpp index 2af2354..e6ec707 100644 --- a/ffmpeg/subtitledecoder.cpp +++ b/ffmpeg/subtitledecoder.cpp @@ -1,7 +1,10 @@ #include "subtitledecoder.h" #include "decodersubtitleframe.hpp" +#include "ffmpegutils.hpp" #include "subtitle.h" +#include + extern "C" { #include } @@ -17,6 +20,23 @@ class SubtitleDecoder::SubtitleDecoderPrivate decoderSubtitleFrame = new DecoderSubtitleFrame(q_ptr); } + void processEvent() + { + while (q_ptr->m_runing.load() && q_ptr->m_eventQueue.size() > 0) { + auto eventPtr = q_ptr->m_eventQueue.take(); + switch (eventPtr->type()) { + case Event::EventType::Pause: decoderSubtitleFrame->addEvent(eventPtr); break; + case Event::EventType::Seek: { + decoderSubtitleFrame->clear(); + decoderSubtitleFrame->addEvent(eventPtr); + auto seekEvent = static_cast(eventPtr.data()); + seekEvent->countDown(); + } break; + default: break; + } + } + } + SubtitleDecoder *q_ptr; DecoderSubtitleFrame *decoderSubtitleFrame; @@ -27,7 +47,10 @@ SubtitleDecoder::SubtitleDecoder(QObject *parent) , d_ptr(new SubtitleDecoderPrivate(this)) {} -SubtitleDecoder::~SubtitleDecoder() = default; +SubtitleDecoder::~SubtitleDecoder() +{ + stopDecoder(); +} void SubtitleDecoder::setVideoResolutionRatio(const QSize &size) { @@ -44,6 +67,8 @@ void SubtitleDecoder::runDecoder() d_ptr->decoderSubtitleFrame->startDecoder(m_formatContext, m_contextInfo); while (m_runing) { + d_ptr->processEvent(); + auto packetPtr(m_queue.take()); if (packetPtr.isNull()) { continue; @@ -54,7 +79,7 @@ void SubtitleDecoder::runDecoder() continue; } - Ffmpeg::calculatePts(packetPtr.data(), m_contextInfo); + calculatePts(packetPtr.data(), m_contextInfo); subtitlePtr->setDefault(packetPtr->pts(), packetPtr->duration(), (const char *) packetPtr->avPacket()->data); @@ -62,7 +87,7 @@ void SubtitleDecoder::runDecoder() d_ptr->decoderSubtitleFrame->append(subtitlePtr); } while (m_runing && d_ptr->decoderSubtitleFrame->size() != 0) { - msleep(Sleep_Queue_Full_Milliseconds); + msleep(s_waitQueueEmptyMilliseconds); } d_ptr->decoderSubtitleFrame->stopDecoder(); } diff --git a/ffmpeg/transcode.cc b/ffmpeg/transcode.cc index d2100ed..8c61a93 100644 --- a/ffmpeg/transcode.cc +++ b/ffmpeg/transcode.cc @@ -3,7 +3,7 @@ #include "avcontextinfo.h" #include "averrormanager.hpp" #include "codeccontext.h" -#include "decoder.h" +#include "ffmpegutils.hpp" #include "formatcontext.h" #include "frame.hpp" #include "packet.h" @@ -736,8 +736,8 @@ void Transcode::loop() d_ptr->filterEncodeWriteframe(framePtr.data(), stream_index); } - Ffmpeg::calculatePts(packetPtr.data(), - d_ptr->transcodeContexts.at(stream_index)->decContextInfoPtr.data()); + calculatePts(packetPtr.data(), + d_ptr->transcodeContexts.at(stream_index)->decContextInfoPtr.data()); emit progressChanged(packetPtr->pts() / duration); if (transcodeCtx->decContextInfoPtr->mediaType() == AVMEDIA_TYPE_VIDEO) { d_ptr->fpsPtr->update(); diff --git a/ffmpeg/videodecoder.cpp b/ffmpeg/videodecoder.cpp index bb32375..ed65199 100644 --- a/ffmpeg/videodecoder.cpp +++ b/ffmpeg/videodecoder.cpp @@ -1,8 +1,11 @@ #include "videodecoder.h" #include "avcontextinfo.h" #include "decodervideoframe.h" +#include "ffmpegutils.hpp" #include "videoformat.hpp" +#include + #include namespace Ffmpeg { @@ -10,12 +13,29 @@ namespace Ffmpeg { class VideoDecoder::VideoDecoderPrivate { public: - VideoDecoderPrivate(VideoDecoder *q) + explicit VideoDecoderPrivate(VideoDecoder *q) : q_ptr(q) { decoderVideoFrame = new DecoderVideoFrame(q_ptr); } + void processEvent() + { + while (q_ptr->m_runing.load() && q_ptr->m_eventQueue.size() > 0) { + auto eventPtr = q_ptr->m_eventQueue.take(); + switch (eventPtr->type()) { + case Event::EventType::Pause: decoderVideoFrame->addEvent(eventPtr); break; + case Event::EventType::Seek: { + decoderVideoFrame->clear(); + decoderVideoFrame->addEvent(eventPtr); + auto seekEvent = static_cast(eventPtr.data()); + seekEvent->countDown(); + } break; + default: break; + } + } + } + VideoDecoder *q_ptr; DecoderVideoFrame *decoderVideoFrame; @@ -31,7 +51,10 @@ VideoDecoder::VideoDecoder(QObject *parent) &VideoDecoder::positionChanged); } -VideoDecoder::~VideoDecoder() = default; +VideoDecoder::~VideoDecoder() +{ + stopDecoder(); +} void VideoDecoder::setVideoRenders(QVector videoRenders) { @@ -48,18 +71,20 @@ void VideoDecoder::runDecoder() d_ptr->decoderVideoFrame->startDecoder(m_formatContext, m_contextInfo); while (m_runing) { + d_ptr->processEvent(); + auto packetPtr(m_queue.take()); if (packetPtr.isNull()) { continue; } auto framePtrs = m_contextInfo->decodeFrame(packetPtr); for (const auto &framePtr : framePtrs) { - Ffmpeg::calculatePts(framePtr.data(), m_contextInfo, m_formatContext); + calculatePts(framePtr.data(), m_contextInfo, m_formatContext); d_ptr->decoderVideoFrame->append(framePtr); } } while (m_runing && d_ptr->decoderVideoFrame->size() != 0) { - msleep(Sleep_Queue_Full_Milliseconds); + msleep(s_waitQueueEmptyMilliseconds); } d_ptr->decoderVideoFrame->stopDecoder(); } diff --git a/ffmpeg/videorender/videopreviewwidget.cc b/ffmpeg/videorender/videopreviewwidget.cc index 5426a7d..c51d31e 100644 --- a/ffmpeg/videorender/videopreviewwidget.cc +++ b/ffmpeg/videorender/videopreviewwidget.cc @@ -1,4 +1,5 @@ #include "videopreviewwidget.hpp" +#include "ffmpegutils.hpp" #include #include @@ -111,7 +112,7 @@ class PreviewTask : public QRunnable if (!framePtr->isKey() && framePtr.isNull()) { continue; } - Ffmpeg::calculatePts(framePtr.data(), videoInfo, formatContext); + calculatePts(framePtr.data(), videoInfo, formatContext); auto pts = framePtr->pts(); if (m_timestamp > pts) { continue; diff --git a/utils/boundedblockingqueue.hpp b/utils/boundedblockingqueue.hpp index 0687392..818a386 100644 --- a/utils/boundedblockingqueue.hpp +++ b/utils/boundedblockingqueue.hpp @@ -38,6 +38,32 @@ class BoundedBlockingQueue m_notEmpty.wakeOne(); } + void putHead(const T &x) + { + std::queue temp; + temp.push(x); + QMutexLocker locker(&m_mutex); + while (!m_queue.empty()) { + temp.push(m_queue.front()); + m_queue.pop(); + } + m_queue.swap(temp); + m_notEmpty.wakeOne(); + } + + void putHead(T &&x) + { + std::queue temp; + temp.push(x); + QMutexLocker locker(&m_mutex); + while (!m_queue.empty()) { + temp.push(m_queue.front()); + m_queue.pop(); + } + m_queue.swap(temp); + m_notEmpty.wakeOne(); + } + auto take() -> T { QMutexLocker locker(&m_mutex); diff --git a/utils/countdownlatch.cc b/utils/countdownlatch.cc index 7677407..191ea87 100644 --- a/utils/countdownlatch.cc +++ b/utils/countdownlatch.cc @@ -19,7 +19,7 @@ CountDownLatch::CountDownLatch(int count) d_ptr->count = count; } -CountDownLatch::~CountDownLatch() {} +CountDownLatch::~CountDownLatch() = default; void CountDownLatch::wait() { @@ -38,6 +38,12 @@ void CountDownLatch::countDown() } } +void CountDownLatch::setCount(int count) +{ + QMutexLocker locker(&d_ptr->mutex); + d_ptr->count = count; +} + int CountDownLatch::getCount() const { QMutexLocker locker(&d_ptr->mutex); diff --git a/utils/countdownlatch.hpp b/utils/countdownlatch.hpp index e4d9e5d..f3d03f3 100644 --- a/utils/countdownlatch.hpp +++ b/utils/countdownlatch.hpp @@ -16,6 +16,8 @@ class UTILS_EXPORT CountDownLatch void wait(); void countDown(); + + void setCount(int count); [[nodiscard]] auto getCount() const -> int; private: diff --git a/utils/threadsafequeue.hpp b/utils/threadsafequeue.hpp index b62995b..c15448d 100644 --- a/utils/threadsafequeue.hpp +++ b/utils/threadsafequeue.hpp @@ -27,6 +27,30 @@ class ThreadSafeQueue m_queue.push(std::move(x)); } + void putHead(const T &x) + { + std::queue temp; + temp.push(x); + QMutexLocker locker(&m_mutex); + while (!m_queue.empty()) { + temp.push(m_queue.front()); + m_queue.pop(); + } + m_queue.swap(temp); + } + + void putHead(T &&x) + { + std::queue temp; + temp.push(x); + QMutexLocker locker(&m_mutex); + while (!m_queue.empty()) { + temp.push(m_queue.front()); + m_queue.pop(); + } + m_queue.swap(temp); + } + auto take() -> T { QMutexLocker locker(&m_mutex);