You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
2.8 KiB
92 lines
2.8 KiB
#pragma once |
|
|
|
#include <QObject> |
|
#include <QImage> |
|
#include <QString> |
|
#include <QTimer> |
|
#include <QElapsedTimer> |
|
#include <QAtomicInt> |
|
#include <QAtomicInteger> |
|
#include <deque> |
|
#include <vector> |
|
#include <memory> |
|
#include <utility> |
|
#include <cstdint> |
|
|
|
class QThread; |
|
|
|
/* Плеер видео на FFmpeg (GUI-процесс). Декод в отдельном потоке, |
|
* отрисовка кадров по мастер-часам (аудио-курсор или системный таймер). */ |
|
class VideoPlayerEngine : public QObject { |
|
Q_OBJECT |
|
public: |
|
enum State { Stopped = 0, Playing = 1, Paused = 2, Finished = 3 }; |
|
|
|
explicit VideoPlayerEngine(QObject* parent = nullptr); |
|
~VideoPlayerEngine() override; |
|
|
|
void open(const QString& path); |
|
void play(); |
|
void pause(); |
|
void togglePlayPause(); |
|
void seekMs(qint64 ms); |
|
void stop(); |
|
void close(); |
|
|
|
State state() const { return m_state; } |
|
qint64 durationMs() const { return m_durationMs; } |
|
qint64 positionMs() const { return m_positionMs; } |
|
bool hasAudio() const { return m_hasAudio; } |
|
int videoWidth() const { return m_videoWidth; } |
|
int videoHeight() const { return m_videoHeight; } |
|
QString filePath() const { return m_path; } |
|
QImage currentFrame() const { return m_renderFrame; } |
|
|
|
signals: |
|
void opened(int width, int height, qint64 durationMs, bool hasAudio); |
|
void positionChanged(qint64 ms); |
|
void stateChanged(int state); |
|
void finished(); |
|
void error(const QString& msg); |
|
void framePresented(); |
|
|
|
private slots: |
|
void onWorkerOpened(int width, int height, qint64 durationMs, bool hasAudio); |
|
void onAudioDecoded(std::shared_ptr<std::vector<int16_t>> pcm, int sampleRate, int channels, qint64 durationMs); |
|
void onFrameDecoded(const QImage& image, qint64 ptsMs, int epoch); |
|
void onWorkerError(const QString& msg); |
|
void onWorkerFinished(); |
|
void presentTick(); |
|
|
|
private: |
|
void setState(State s); |
|
qint64 clockMs() const; |
|
void clearPending(); |
|
|
|
QString m_path; |
|
QThread* m_thread = nullptr; |
|
class Worker* m_worker = nullptr; |
|
|
|
State m_state = Stopped; |
|
qint64 m_durationMs = 0; |
|
qint64 m_positionMs = 0; |
|
bool m_hasAudio = false; |
|
int m_videoWidth = 0; |
|
int m_videoHeight = 0; |
|
|
|
/* управление worker'ом (атомики, доступны из обоих потоков) */ |
|
QAtomicInt m_abort{0}; |
|
QAtomicInt m_paused{0}; |
|
QAtomicInt m_seekReq{0}; |
|
QAtomicInteger<qint64> m_seekTargetMs{0}; |
|
QAtomicInt m_buffered{0}; |
|
QAtomicInt m_epoch{0}; |
|
|
|
/* презентация (только GUI-поток) */ |
|
std::deque<std::pair<QImage, qint64>> m_pending; |
|
QImage m_renderFrame; |
|
QTimer m_presentTimer; |
|
int m_audioSampleRate = 0; |
|
QElapsedTimer m_elapsed; |
|
qint64 m_elapsedBaseMs = 0; |
|
};
|
|
|