diff --git a/examples/player/controlwidget.cc b/examples/player/controlwidget.cc index d9c507e..ab1e7a0 100644 --- a/examples/player/controlwidget.cc +++ b/examples/player/controlwidget.cc @@ -216,7 +216,7 @@ void ControlWidget::setPosition(int value) d_ptr->slider->blockSignals(false); } -void ControlWidget::onReadSpeedChanged(qint64 speed) +void ControlWidget::setCacheSpeed(qint64 speed) { d_ptr->readSpeedLabel->setText(Utils::convertBytesToString(speed) + "/S"); } diff --git a/examples/player/controlwidget.hpp b/examples/player/controlwidget.hpp index edac6c2..c1f3ada 100644 --- a/examples/player/controlwidget.hpp +++ b/examples/player/controlwidget.hpp @@ -27,8 +27,7 @@ class ControlWidget : public QWidget void setVolume(int value); [[nodiscard]] auto volume() const -> int; -public slots: - void onReadSpeedChanged(qint64 speed); + void setCacheSpeed(qint64 speed); signals: void previous(); diff --git a/examples/player/mainwindow.cpp b/examples/player/mainwindow.cpp index c8b9e46..6781904 100644 --- a/examples/player/mainwindow.cpp +++ b/examples/player/mainwindow.cpp @@ -7,6 +7,7 @@ #include "titlewidget.hpp" #include +#include #include #include #include @@ -121,7 +122,28 @@ class MainWindow::MainWindowPrivate titleWidget->setVisible(visible); } + void started() + { + controlWidget->setSourceFPS(playerPtr->fps()); + + auto size = playerPtr->resolutionRatio(); + q_ptr->setWindowTitle(QString("%1[%2x%3]") + .arg(playlistModel->playlist()->currentMedia().fileName(), + QString::number(size.width()), + QString::number(size.height()))); + + fpsTimer->start(1000); + } + + void finished() + { + fpsTimer->stop(); + controlWidget->setDuration(0); + controlWidget->setPosition(0); + } + MainWindow *q_ptr; + QScopedPointer playerPtr; QScopedPointer videoRender; QScopedPointer videoPreviewWidgetPtr; @@ -177,26 +199,6 @@ void MainWindow::onError(const Ffmpeg::AVError &avError) qWarning() << str; } -void MainWindow::onStarted() -{ - d_ptr->controlWidget->setSourceFPS(d_ptr->playerPtr->fps()); - - auto size = d_ptr->playerPtr->resolutionRatio(); - setWindowTitle(QString("%1[%2x%3]") - .arg(d_ptr->playlistModel->playlist()->currentMedia().fileName(), - QString::number(size.width()), - QString::number(size.height()))); - - d_ptr->fpsTimer->start(1000); -} - -void MainWindow::onFinished() -{ - d_ptr->fpsTimer->stop(); - d_ptr->controlWidget->setDuration(0); - d_ptr->controlWidget->setPosition(0); -} - void MainWindow::onHoverSlider(int pos, int value) { auto index = d_ptr->playerPtr->videoIndex(); @@ -250,12 +252,14 @@ void MainWindow::onShowCurrentFPS() void MainWindow::onOpenLocalMedia() { - const QString path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) - .value(0, QDir::homePath()); + const auto path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) + .value(0, QDir::homePath()); + const auto filter = tr("Media (*.mp4 *.flv *.ts *.avi *.rmvb *.mkv *.wmv *.mp3 *.wav *.flac " + "*.ape *.m4a *.aac *.ogg *.ac3 *.mpg)"); const auto urls = QFileDialog::getOpenFileUrls(this, - tr("Open File"), - path, - tr("Audio Video (*.mp3 *.mp4 *.mkv *.rmvb)")); + tr("Open Media"), + QUrl::fromUserInput(path), + filter); if (urls.isEmpty()) { return; } @@ -309,6 +313,48 @@ void MainWindow::jump(const QModelIndex &index) } } +void MainWindow::onProcessEvents() +{ + while (d_ptr->playerPtr->eventCount() > 0) { + auto eventPtr = d_ptr->playerPtr->takeEvent(); + switch (eventPtr->type()) { + case Ffmpeg::Event::EventType::DurationChanged: { + auto duration = static_cast(eventPtr.data()); + d_ptr->controlWidget->setDuration(duration->duration() / AV_TIME_BASE); + } break; + case Ffmpeg::Event::EventType::PositionChanged: { + auto position = static_cast(eventPtr.data()); + d_ptr->controlWidget->setPosition(position->position() / AV_TIME_BASE); + } break; + case Ffmpeg::Event::EventType::MediaStateChanged: { + auto state = static_cast(eventPtr.data()); + switch (state->state()) { + case Ffmpeg::MediaState::Stopped: + d_ptr->controlWidget->setPlayButtonChecked(false); + d_ptr->finished(); + break; + case Ffmpeg::MediaState::Pausing: + d_ptr->controlWidget->setPlayButtonChecked(false); + break; + case Ffmpeg::MediaState::Opening: + d_ptr->controlWidget->setPlayButtonChecked(true); + break; + case Ffmpeg::MediaState::Playing: + d_ptr->controlWidget->setPlayButtonChecked(true); + d_ptr->started(); + break; + default: break; + } + } break; + case Ffmpeg::Event::EventType::CacheSpeedChanged: { + auto speed = static_cast(eventPtr.data()); + d_ptr->controlWidget->setCacheSpeed(speed->speed()); + } break; + default: break; + } + } +} + bool MainWindow::eventFilter(QObject *watched, QEvent *event) { if (!d_ptr->videoRender.isNull() && watched == d_ptr->videoRender->widget()) { @@ -406,14 +452,6 @@ void MainWindow::setupUI() void MainWindow::buildConnect() { connect(d_ptr->playerPtr.data(), &Ffmpeg::Player::error, this, &MainWindow::onError); - connect(d_ptr->playerPtr.data(), - &Ffmpeg::Player::durationChanged, - d_ptr->controlWidget, - [this](qint64 duration) { d_ptr->controlWidget->setDuration(duration / AV_TIME_BASE); }); - connect(d_ptr->playerPtr.data(), - &Ffmpeg::Player::positionChanged, - d_ptr->controlWidget, - [this](qint64 position) { d_ptr->controlWidget->setPosition(position / AV_TIME_BASE); }); connect(d_ptr->playerPtr.data(), &Ffmpeg::Player::audioTracksChanged, d_ptr->controlWidget, @@ -464,27 +502,11 @@ void MainWindow::buildConnect() break; } }); - connect(d_ptr->playerPtr.data(), &Ffmpeg::Player::playStarted, this, &MainWindow::onStarted); - connect(d_ptr->playerPtr.data(), &Ffmpeg::Player::finished, this, &MainWindow::onFinished); - connect(d_ptr->playerPtr.data(), - &Ffmpeg::Player::stateChanged, - d_ptr->controlWidget, - [this](Ffmpeg::Player::MediaState state) { - switch (state) { - case Ffmpeg::Player::MediaState::StoppedState: - case Ffmpeg::Player::MediaState::PausedState: - d_ptr->controlWidget->setPlayButtonChecked(false); - break; - case Ffmpeg::Player::MediaState::PlayingState: - d_ptr->controlWidget->setPlayButtonChecked(true); - break; - default: break; - } - }); + connect(d_ptr->playerPtr.data(), - &Ffmpeg::Player::readSpeedChanged, - d_ptr->controlWidget, - &ControlWidget::onReadSpeedChanged); + &Ffmpeg::Player::eventIncrease, + this, + &MainWindow::onProcessEvents); connect(d_ptr->controlWidget, &ControlWidget::previous, diff --git a/examples/player/mainwindow.h b/examples/player/mainwindow.h index 8615d7f..8bff4df 100644 --- a/examples/player/mainwindow.h +++ b/examples/player/mainwindow.h @@ -16,8 +16,6 @@ class MainWindow : public QMainWindow private slots: void onError(const Ffmpeg::AVError &averror); - void onStarted(); - void onFinished(); void onHoverSlider(int pos, int value); void onLeaveSlider(); void onShowCurrentFPS(); @@ -29,6 +27,8 @@ private slots: void playlistPositionChanged(int); void jump(const QModelIndex &index); + void onProcessEvents(); + protected: auto eventFilter(QObject *watched, QEvent *event) -> bool override; void keyPressEvent(QKeyEvent *ev) override; diff --git a/ffmpeg/CMakeLists.txt b/ffmpeg/CMakeLists.txt index 1d4af4e..39954ea 100644 --- a/ffmpeg/CMakeLists.txt +++ b/ffmpeg/CMakeLists.txt @@ -1,4 +1,10 @@ set(PROJECT_SOURCES + event/errorevent.hpp + event/event.hpp + event/pauseevent.hpp + event/seekevent.hpp + event/trackevent.hpp + event/valueevent.hpp filter/filtercontext.cc filter/filtercontext.hpp filter/filtergraph.cc @@ -50,8 +56,6 @@ set(PROJECT_SOURCES decodersubtitleframe.hpp decodervideoframe.cpp decodervideoframe.h - event.cc - event.hpp ffmepg_global.h ffmpegutils.cc ffmpegutils.hpp @@ -59,6 +63,7 @@ set(PROJECT_SOURCES formatcontext.h frame.cc frame.hpp + mediainfo.hpp packet.cpp packet.h player.cpp diff --git a/ffmpeg/clock.cc b/ffmpeg/clock.cc index 6fb7624..1547302 100644 --- a/ffmpeg/clock.cc +++ b/ffmpeg/clock.cc @@ -19,11 +19,11 @@ class Clock::ClockPrivate 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; // 是否暂停播放 + qint64 pts = 0; // 当前 AVFrame 的时间戳 microseconds + qint64 pts_drift = 0; // 时钟漂移量,用于计算当前时钟的状态 microseconds + qint64 last_updated = 0; // 上一次更新时钟状态的时间 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}; @@ -44,29 +44,41 @@ Clock::Clock(QObject *parent) Clock::~Clock() = default; -void Clock::reset() +void Clock::reset(qint64 pts) { QMutexLocker locker(&d_ptr->mutex); - d_ptr->pts = 0; + d_ptr->pts = pts; 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 +void Clock::invalidate() +{ + QMutexLocker locker(&d_ptr->mutex); + d_ptr->last_updated = 0; +} + +auto Clock::isVaild() const -> bool +{ + QMutexLocker locker(&d_ptr->mutex); + return d_ptr->last_updated != 0; +} + +auto Clock::pts() const -> qint64 { QMutexLocker locker(&d_ptr->mutex); return d_ptr->pts; } -auto Clock::ptsDrift() -> qint64 +auto Clock::ptsDrift() const -> qint64 { QMutexLocker locker(&d_ptr->mutex); return d_ptr->pts_drift; } -auto Clock::lastUpdated() -> qint64 +auto Clock::lastUpdated() const -> qint64 { QMutexLocker locker(&d_ptr->mutex); return d_ptr->last_updated; @@ -78,13 +90,13 @@ void Clock::resetSerial() d_ptr->serial = Clock::ClockPrivate::s_serial.load(); } -auto Clock::serial() -> qint64 +auto Clock::serial() const -> qint64 { QMutexLocker locker(&d_ptr->mutex); return d_ptr->serial; } -auto Clock::paused() -> bool +auto Clock::paused() const -> bool { QMutexLocker locker(&d_ptr->mutex); return d_ptr->paused; @@ -108,6 +120,8 @@ void Clock::update(qint64 pts, qint64 time) if (this == Clock::ClockPrivate::s_clock) { qint64 timediff = (time - d_ptr->last_updated) * speed(); d_ptr->pts_drift += pts - d_ptr->pts - timediff; + } else if (Clock::ClockPrivate::s_clock->d_ptr->last_updated == 0) { + d_ptr->pts_drift = pts; } else { auto *masterClock = Clock::ClockPrivate::s_clock; auto masterClockPts = masterClock->d_ptr->pts - masterClock->d_ptr->pts_drift; @@ -119,7 +133,7 @@ void Clock::update(qint64 pts, qint64 time) d_ptr->last_updated = time; } -auto Clock::getDelayWithMaster(qint64 &delay) -> bool +auto Clock::getDelayWithMaster(qint64 &delay) const -> bool { if (serial() != Clock::ClockPrivate::s_serial.load()) { return false; @@ -177,4 +191,9 @@ void Clock::setMaster(Clock *clock) Clock::ClockPrivate::s_clock = clock; } +auto Clock::master() -> Clock * +{ + return Clock::ClockPrivate::s_clock; +} + } // namespace Ffmpeg diff --git a/ffmpeg/clock.hpp b/ffmpeg/clock.hpp index 8e17524..2827eab 100644 --- a/ffmpeg/clock.hpp +++ b/ffmpeg/clock.hpp @@ -11,22 +11,25 @@ class Clock : public QObject explicit Clock(QObject *parent = nullptr); ~Clock() override; - void reset(); + void reset(qint64 pts); - auto pts() -> qint64; - auto ptsDrift() -> qint64; - auto lastUpdated() -> qint64; + void invalidate(); + [[nodiscard]] auto isVaild() const -> bool; + + [[nodiscard]] auto pts() const -> qint64; + [[nodiscard]] auto ptsDrift() const -> qint64; + [[nodiscard]] auto lastUpdated() const -> qint64; void resetSerial(); - auto serial() -> qint64; + [[nodiscard]] auto serial() const -> qint64; void setPaused(bool value); - auto paused() -> bool; + [[nodiscard]] auto paused() const -> bool; void update(qint64 pts, qint64 time); // return true if delay is valid - auto getDelayWithMaster(qint64 &delay) -> bool; + auto getDelayWithMaster(qint64 &delay) const -> bool; // return true if delay is valid static auto adjustDelay(qint64 &delay) -> bool; @@ -41,6 +44,7 @@ class Clock : public QObject // not thread safe and not delete clock static void setMaster(Clock *clock); + static auto master() -> Clock *; private: class ClockPrivate; diff --git a/ffmpeg/decoderaudioframe.cpp b/ffmpeg/decoderaudioframe.cpp index 19f7b19..f253b20 100644 --- a/ffmpeg/decoderaudioframe.cpp +++ b/ffmpeg/decoderaudioframe.cpp @@ -96,13 +96,16 @@ void DecoderAudioFrame::runDecoder() QAudioDevice audioDevice(QMediaDevices::defaultAudioOutput()); auto format = resetAudioOutput(); AudioFrameConverter audioConverter(m_contextInfo->codecCtx(), format); - d_ptr->clock->reset(); + bool firstFrame = false; while (m_runing.load()) { checkDefaultAudioOutput(audioDevice); auto framePtr(m_queue.take()); if (framePtr.isNull()) { continue; + } else if (!firstFrame) { + firstFrame = true; + d_ptr->clock->reset(framePtr->pts()); } auto audioBuf = audioConverter.convert(framePtr.data()); auto pts = framePtr->pts(); diff --git a/ffmpeg/decodersubtitleframe.cc b/ffmpeg/decodersubtitleframe.cc index f5ebd6f..fee1e24 100644 --- a/ffmpeg/decodersubtitleframe.cc +++ b/ffmpeg/decodersubtitleframe.cc @@ -68,11 +68,14 @@ void DecoderSubtitleFrame::runDecoder() } assPtr->setWindowSize(d_ptr->videoResolutionRatio); SwsContext *swsContext = nullptr; - d_ptr->clock->reset(); + bool firstFrame = false; while (m_runing.load()) { auto subtitlePtr(m_queue.take()); if (subtitlePtr.isNull()) { continue; + } else if (!firstFrame) { + firstFrame = true; + d_ptr->clock->reset(subtitlePtr->pts()); } subtitlePtr->setVideoResolutionRatio(d_ptr->videoResolutionRatio); subtitlePtr->parse(swsContext); diff --git a/ffmpeg/decodervideoframe.cpp b/ffmpeg/decodervideoframe.cpp index e4bd8f9..1975108 100644 --- a/ffmpeg/decodervideoframe.cpp +++ b/ffmpeg/decodervideoframe.cpp @@ -57,11 +57,14 @@ void DecoderVideoFrame::runDecoder() render->resetFps(); } quint64 dropNum = 0; - d_ptr->clock->reset(); + bool firstFrame = false; while (m_runing.load()) { auto framePtr(m_queue.take()); if (framePtr.isNull()) { continue; + } else if (!firstFrame) { + firstFrame = true; + d_ptr->clock->reset(framePtr->pts()); } auto pts = framePtr->pts(); auto emitPosition = qScopeGuard([=]() { emit positionChanged(pts); }); diff --git a/ffmpeg/event.cc b/ffmpeg/event.cc deleted file mode 100644 index 96a8bc0..0000000 --- a/ffmpeg/event.cc +++ /dev/null @@ -1,38 +0,0 @@ -#include "event.hpp" - -namespace Ffmpeg { - -class Event::EventPrivate -{ -public: - explicit EventPrivate(Event *q) - : q_ptr(q) - {} - - Event *q_ptr; - - EventType type; - QVariantList args; -}; - -Event::Event(EventType type, const QVariantList &args, QObject *parent) - : QObject(parent) - , d_ptr(new EventPrivate(this)) -{ - d_ptr->type = type; - d_ptr->args = std::move(args); -} - -Event::~Event() = default; - -auto Event::type() const -> EventType -{ - return d_ptr->type; -} - -auto Event::args() const -> QVariantList -{ - return d_ptr->args; -} - -} // namespace Ffmpeg diff --git a/ffmpeg/event.hpp b/ffmpeg/event.hpp deleted file mode 100644 index 516b34e..0000000 --- a/ffmpeg/event.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -namespace Ffmpeg { - -class Event : public QObject -{ -public: - enum EventType { - None, - OpenMedia, - Seek, - Pause, - Stop, - Volume, - Speed, - AudioTrack, - SubtitleTrack, - VideoRenders, - UseGpuDecode, - }; - Q_ENUM(EventType); - - explicit Event(EventType type, const QVariantList &args = {}, QObject *parent = nullptr); - ~Event() override; - - [[nodiscard]] auto type() const -> EventType; - [[nodiscard]] auto args() const -> QVariantList; - -private: - class EventPrivate; - QScopedPointer d_ptr; -}; - -using EventPtr = QSharedPointer; - -} // namespace Ffmpeg diff --git a/ffmpeg/event/errorevent.hpp b/ffmpeg/event/errorevent.hpp new file mode 100644 index 0000000..569bb53 --- /dev/null +++ b/ffmpeg/event/errorevent.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "event.hpp" + +#include + +namespace Ffmpeg { + +class FFMPEG_EXPORT ErrorEvent : public Event +{ +public: + explicit ErrorEvent(const AVError &error, QObject *parent = nullptr) + : Event(parent) + , m_error(error) + {} + + [[nodiscard]] auto type() const -> EventType override { return Error; } + + void setError(const AVError &error) { m_error = error; } + [[nodiscard]] auto error() const -> AVError { return m_error; } + +private: + AVError m_error; +}; + +} // namespace Ffmpeg diff --git a/ffmpeg/event/event.hpp b/ffmpeg/event/event.hpp new file mode 100644 index 0000000..4987e18 --- /dev/null +++ b/ffmpeg/event/event.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace Ffmpeg { + +class FFMPEG_EXPORT Event : public QObject +{ + Q_OBJECT +public: + enum EventType { + None, + AudioTracksChanged, + VideoTracksChanged, + SubtitleTracksChanged, + AudioTrackChanged, + VideoTrackChanged, + SubtitleTrackChanged, + DurationChanged, + PositionChanged, + MediaStateChanged, + CacheSpeedChanged, + Error, + + Pause = 100, + Seek, + seekRelative, + }; + Q_ENUM(EventType); + + using QObject::QObject; + + bool operator==(const Event &other) const { return type() == other.type(); } + bool operator!=(const Event &other) const { return !(*this == other); } + + [[nodiscard]] virtual auto type() const -> EventType { return None; } +}; + +using EventPtr = QSharedPointer; + +} // namespace Ffmpeg diff --git a/ffmpeg/event/event.pri b/ffmpeg/event/event.pri new file mode 100644 index 0000000..4a1be02 --- /dev/null +++ b/ffmpeg/event/event.pri @@ -0,0 +1,7 @@ +HEADERS += \ + $$PWD/errorevent.hpp \ + $$PWD/event.hpp \ + $$PWD/pauseevent.hpp \ + $$PWD/seekevent.hpp \ + $$PWD/trackevent.hpp \ + $$PWD/valueevent.hpp diff --git a/ffmpeg/event/pauseevent.hpp b/ffmpeg/event/pauseevent.hpp new file mode 100644 index 0000000..020fa30 --- /dev/null +++ b/ffmpeg/event/pauseevent.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "event.hpp" + +namespace Ffmpeg { + +class FFMPEG_EXPORT PauseEvent : public Event +{ +public: + explicit PauseEvent(bool paused, QObject *parent = nullptr) + : Event(parent) + , m_paused(paused) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::Pause; } + + void setPaused(bool paused) { m_paused = paused; } + [[nodiscard]] auto paused() const -> bool { return m_paused; } + +private: + bool m_paused = false; +}; + +} // namespace Ffmpeg diff --git a/ffmpeg/event/seekevent.hpp b/ffmpeg/event/seekevent.hpp new file mode 100644 index 0000000..5220228 --- /dev/null +++ b/ffmpeg/event/seekevent.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "event.hpp" + +namespace Ffmpeg { + +class FFMPEG_EXPORT SeekEvent : public Event +{ +public: + explicit SeekEvent(qint64 position, QObject *parent = nullptr) + : Event(parent) + , m_position(position) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::Seek; } + + void setPosition(qint64 position) { m_position = position; } + [[nodiscard]] auto position() const -> qint64 { return m_position; } + +private: + qint64 m_position = 0; +}; + +} // namespace Ffmpeg diff --git a/ffmpeg/event/trackevent.hpp b/ffmpeg/event/trackevent.hpp new file mode 100644 index 0000000..31badfa --- /dev/null +++ b/ffmpeg/event/trackevent.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "event.hpp" + +namespace Ffmpeg { + +class FFMPEG_EXPORT TracksChangedEvent : public Event +{ +public: + TracksChangedEvent(const QStringList &tracks, EventType type, QObject *parent = nullptr) + : Event(parent) + , m_tracks(tracks) + { + setType(type); + } + + void setType(EventType type) + { + Q_ASSERT(type == EventType::AudioTracksChanged || type == EventType::VideoTracksChanged + || type == EventType::SubtitleTracksChanged); + m_type = type; + } + [[nodiscard]] auto type() const -> EventType override { return m_type; } + + void setTracks(const QStringList &tracks) { m_tracks = tracks; } + [[nodiscard]] auto tracks() const -> QStringList { return m_tracks; } + +private: + EventType m_type = EventType::None; + QStringList m_tracks; +}; + +class FFMPEG_EXPORT TrackChangedEvent : public Event +{ +public: + TrackChangedEvent(const QString &track, EventType type, QObject *parent = nullptr) + : Event(parent) + , m_track(track) + { + setType(type); + } + + void setType(EventType type) + { + Q_ASSERT(type == EventType::AudioTrackChanged || type == EventType::VideoTrackChanged + || type == EventType::SubtitleTrackChanged); + m_type = type; + } + [[nodiscard]] auto type() const -> EventType override { return m_type; } + + void setTrack(const QString &track) { m_track = track; } + [[nodiscard]] auto track() const -> QString { return m_track; } + +private: + EventType m_type = EventType::None; + QString m_track; +}; + +} // namespace Ffmpeg diff --git a/ffmpeg/event/valueevent.hpp b/ffmpeg/event/valueevent.hpp new file mode 100644 index 0000000..f1f4a0c --- /dev/null +++ b/ffmpeg/event/valueevent.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include "event.hpp" + +#include + +namespace Ffmpeg { + +class FFMPEG_EXPORT DurationChangedEvent : public Event +{ +public: + explicit DurationChangedEvent(qint64 duration, QObject *parent = nullptr) + : Event(parent) + , m_duration(duration) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::DurationChanged; } + + void setDuration(qint64 duration) { m_duration = duration; } + [[nodiscard]] auto duration() const -> qint64 { return m_duration; } + +private: + qint64 m_duration = 0; +}; + +class FFMPEG_EXPORT PositionChangedEvent : public Event +{ +public: + explicit PositionChangedEvent(qint64 position, QObject *parent = nullptr) + : Event(parent) + , m_position(position) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::PositionChanged; } + + void setPosition(qint64 position) { m_position = position; } + [[nodiscard]] auto position() const -> qint64 { return m_position; } + +private: + qint64 m_position = 0; +}; + +class FFMPEG_EXPORT MediaStateChangedEvent : public Event +{ +public: + explicit MediaStateChangedEvent(MediaState state, QObject *parent = nullptr) + : Event(parent) + , m_state(state) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::MediaStateChanged; } + + void setState(MediaState state) { m_state = state; } + [[nodiscard]] auto state() const -> MediaState { return m_state; } + +private: + MediaState m_state = MediaState::Stopped; +}; + +class FFMPEG_EXPORT CacheSpeedChangedEvent : public Event +{ +public: + explicit CacheSpeedChangedEvent(qint64 speed, QObject *parent = nullptr) + : Event(parent) + , m_speed(speed) + {} + + [[nodiscard]] auto type() const -> EventType override { return EventType::CacheSpeedChanged; } + + void setSpeed(qint64 speed) { m_speed = speed; } + [[nodiscard]] auto speed() const -> qint64 { return m_speed; } + +private: + qint64 m_speed = 0; +}; + +} // namespace Ffmpeg diff --git a/ffmpeg/ffmpeg.pro b/ffmpeg/ffmpeg.pro index aecd18f..cbae826 100644 --- a/ffmpeg/ffmpeg.pro +++ b/ffmpeg/ffmpeg.pro @@ -4,6 +4,7 @@ include(videorender/videorender.pri) include(subtitle/subtitle.pri) include(filter/filter.pri) include(gpu/gpu.pri) +include(event/event.pri) QT += widgets multimedia openglwidgets @@ -25,7 +26,6 @@ SOURCES += \ decoderaudioframe.cpp \ decodersubtitleframe.cc \ decodervideoframe.cpp \ - event.cc \ ffmpegutils.cc \ formatcontext.cpp \ frame.cc \ @@ -52,11 +52,11 @@ HEADERS += \ decoderaudioframe.h \ decodersubtitleframe.hpp \ decodervideoframe.h \ - event.hpp \ ffmepg_global.h \ ffmpegutils.hpp \ formatcontext.h \ frame.hpp \ + mediainfo.hpp \ packet.h \ player.h \ subtitle.h \ diff --git a/ffmpeg/mediainfo.hpp b/ffmpeg/mediainfo.hpp new file mode 100644 index 0000000..d11ee55 --- /dev/null +++ b/ffmpeg/mediainfo.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace Ffmpeg { + +enum MediaState { Stopped, Opening, Playing, Pausing }; + +} // namespace Ffmpeg diff --git a/ffmpeg/player.cpp b/ffmpeg/player.cpp index ac535e3..74ed9d6 100644 --- a/ffmpeg/player.cpp +++ b/ffmpeg/player.cpp @@ -2,19 +2,20 @@ #include "audiodecoder.h" #include "avcontextinfo.h" #include "averrormanager.hpp" +#include "clock.hpp" #include "codeccontext.h" #include "formatcontext.h" #include "packet.h" #include "subtitledecoder.h" #include "videodecoder.h" +#include +#include #include #include #include -#include - extern "C" { #include } @@ -40,7 +41,7 @@ class Player::PlayerPrivate auto initAvCodec() -> bool { - isopen = false; + isOpen = false; //初始化pFormatCtx结构 if (!formatCtx->openFilePath(filepath)) { @@ -52,7 +53,7 @@ class Player::PlayerPrivate return false; } - emit q_ptr->durationChanged(formatCtx->duration()); + addEvent(new DurationChangedEvent(formatCtx->duration())); q_ptr->onPositionChanged(0); audioInfo->resetIndex(); @@ -98,7 +99,7 @@ class Player::PlayerPrivate return false; } - isopen = true; + isOpen = true; // formatCtx->printFileInfo(); formatCtx->dumpFormat(); return true; @@ -106,7 +107,9 @@ class Player::PlayerPrivate void playVideo() { - Q_ASSERT(isopen); + Q_ASSERT(isOpen); + + setMediaState(Playing); formatCtx->discardStreamExcluded( {audioInfo->index(), videoInfo->index(), subtitleInfo->index()}); @@ -123,8 +126,7 @@ class Player::PlayerPrivate } else { Q_ASSERT(false); } - - emit q_ptr->playStarted(); + Clock::master()->invalidate(); qint64 readSize = 0; QElapsedTimer elapsedTimer; @@ -160,6 +162,8 @@ class Player::PlayerPrivate audioDecoder->stopDecoder(); qInfo() << "play finish"; + + setMediaState(Stopped); } auto setMediaIndex(AVContextInfo *contextInfo, int index) -> bool @@ -173,10 +177,10 @@ class Player::PlayerPrivate : AVContextInfo::GpuType::NotUseGpu); } - void setMediaState(MediaState mediaState) + void setMediaState(MediaState mediaState_) { - mediaState = mediaState; - emit q_ptr->stateChanged(mediaState); + mediaState = mediaState_; + addEvent(new MediaStateChangedEvent(mediaState)); } QSize resolutionRatio() const @@ -191,13 +195,22 @@ class Player::PlayerPrivate if (elapsed < 5000) { return; } - auto speed = readSize * 1000 / elapsed; - emit q_ptr->readSpeedChanged(speed); + addEvent(new CacheSpeedChangedEvent(speed)); timer.restart(); readSize = 0; } + void addEvent(Event *event) + { + EventPtr eventPtr(event); + eventQueue.put(eventPtr); + while (eventQueue.size() > maxEventCount) { + eventQueue.take(); + } + emit q_ptr->eventIncrease(); + } + Player *q_ptr; FormatContext *formatCtx; @@ -210,12 +223,15 @@ class Player::PlayerPrivate SubtitleDecoder *subtitleDecoder; QString filepath; - std::atomic_bool isopen = true; + std::atomic_bool isOpen = true; std::atomic_bool runing = true; bool gpuDecode = false; qint64 position = 0; - std::atomic mediaState = Player::MediaState::StoppedState; + std::atomic mediaState = MediaState::Stopped; + + Utils::ThreadSafeQueue eventQueue; + int maxEventCount = 100; QVector videoRenders = {}; }; @@ -244,8 +260,10 @@ void Player::openMedia(const QString &filepath) { onStop(); d_ptr->filepath = filepath; + d_ptr->setMediaState(Opening); d_ptr->initAvCodec(); - if (!d_ptr->isopen) { + if (!d_ptr->isOpen) { + d_ptr->setMediaState(Stopped); qWarning() << "initAvCode Error"; return; } @@ -255,7 +273,6 @@ void Player::openMedia(const QString &filepath) void Player::onPlay() { buildConnect(true); - d_ptr->setMediaState(MediaState::PlayingState); d_ptr->runing = true; start(); } @@ -278,7 +295,6 @@ void Player::onStop() render->resetAllFrame(); } d_ptr->formatCtx->close(); - d_ptr->setMediaState(MediaState::StoppedState); } void Player::onPositionChanged(qint64 position) @@ -288,7 +304,7 @@ void Player::onPositionChanged(qint64 position) return; } d_ptr->position = position; - emit positionChanged(position); + d_ptr->addEvent(new PositionChangedEvent(position)); } void Player::seek(qint64 position) @@ -351,7 +367,7 @@ void Player::setSubtitleTrack(const QString &text) bool Player::isOpen() { - return d_ptr->isopen; + return d_ptr->isOpen; } void Player::setVolume(qreal volume) @@ -378,11 +394,11 @@ double Player::speed() void Player::pause(bool status) { if (status) { - d_ptr->setMediaState(MediaState::PausedState); + d_ptr->setMediaState(MediaState::Pausing); } else if (isRunning()) { - d_ptr->setMediaState(MediaState::PlayingState); + d_ptr->setMediaState(d_ptr->isOpen ? MediaState::Playing : MediaState::Opening); } else { - d_ptr->setMediaState(MediaState::StoppedState); + d_ptr->setMediaState(MediaState::Stopped); } } @@ -417,7 +433,7 @@ bool Player::isGpuDecode() return d_ptr->gpuDecode; } -Player::MediaState Player::mediaState() +MediaState Player::mediaState() { return d_ptr->mediaState; } @@ -474,9 +490,19 @@ QVector Player::videoRenders() return d_ptr->videoRenders; } +size_t Player::eventCount() const +{ + return d_ptr->eventQueue.size(); +} + +EventPtr Player::takeEvent() +{ + return d_ptr->eventQueue.take(); +} + void Player::run() { - if (!d_ptr->isopen) { + if (!d_ptr->isOpen) { return; } d_ptr->playVideo(); diff --git a/ffmpeg/player.h b/ffmpeg/player.h index d57f7c8..8111367 100644 --- a/ffmpeg/player.h +++ b/ffmpeg/player.h @@ -2,6 +2,9 @@ #define PLAYER_H #include "ffmepg_global.h" +#include "mediainfo.hpp" + +#include #include @@ -15,8 +18,6 @@ class FFMPEG_EXPORT Player : public QThread { Q_OBJECT public: - enum MediaState { StoppedState, PlayingState, PausedState }; - explicit Player(QObject *parent = nullptr); ~Player() override; @@ -55,6 +56,9 @@ class FFMPEG_EXPORT Player : public QThread void setVideoRenders(QVector videoRenders); QVector videoRenders(); + size_t eventCount() const; + EventPtr takeEvent(); + public slots: void onPlay(); void onStop(); @@ -63,19 +67,13 @@ private slots: void onPositionChanged(qint64 position); signals: - void durationChanged(qint64 duration); // microsecond - void positionChanged(qint64 position); // microsecond - void stateChanged(Ffmpeg::Player::MediaState); void audioTracksChanged(const QStringList &tracks); void audioTrackChanged(const QString &track); void subTracksChanged(const QStringList &streams); void subTrackChanged(const QString &stream); void error(const Ffmpeg::AVError &avError); - void playStarted(); - void seekFinished(); - - void readSpeedChanged(qint64); + void eventIncrease(); protected: void run() override; diff --git a/utils/threadsafequeue.hpp b/utils/threadsafequeue.hpp index a40a4dc..b62995b 100644 --- a/utils/threadsafequeue.hpp +++ b/utils/threadsafequeue.hpp @@ -49,13 +49,13 @@ class ThreadSafeQueue } } - auto size() const -> int + [[nodiscard]] auto size() const -> int { QMutexLocker locker(&m_mutex); return m_queue.size(); } - auto empty() const -> bool + [[nodiscard]] auto empty() const -> bool { QMutexLocker locker(&m_mutex); return m_queue.empty();