#pragma once #include #include #include #include #include #include #include #include #include #include #include #include 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> 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 m_seekTargetMs{0}; QAtomicInt m_buffered{0}; QAtomicInt m_epoch{0}; /* презентация (только GUI-поток) */ std::deque> m_pending; QImage m_renderFrame; QTimer m_presentTimer; int m_audioSampleRate = 0; QElapsedTimer m_elapsed; qint64 m_elapsedBaseMs = 0; };