97 lines
2.9 KiB
C++
97 lines
2.9 KiB
C++
#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;
|
|
|
|
Q_DECLARE_METATYPE(std::shared_ptr<std::vector<int16_t>>)
|
|
|
|
/* Плеер видео на 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();
|
|
|
|
uint64_t m_openGeneration = 0;
|
|
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;
|
|
uint64_t m_audioPlayback = 0;
|
|
int m_audioSampleRate = 0;
|
|
QElapsedTimer m_elapsed;
|
|
qint64 m_elapsedBaseMs = 0;
|
|
};
|