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

#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;
};