/* Просмотр live: audio worker получает батчи и готовит PCM, отдельный worker владеет FFmpeg. * GUI владеет окном; общий output смешивает PCM, callback сохраняет отдельные часы этой трансляции. * Закрытие отсоединяет output, останавливает оба worker и снимает reader. */ #pragma once #include #include #include #include class VideoRenderWidget; class QLabel; class QCloseEvent; class QKeyEvent; class StreamPlayerWindow : public QWidget { Q_OBJECT public: explicit StreamPlayerWindow(uint64_t group, uint64_t source, uint64_t epoch, const QString& name, QWidget* parent = nullptr, bool embedded = false); ~StreamPlayerWindow() override; void stop(); bool finished() const; bool failed() const; int drainTimeoutMs() const; // Остаток готового media + 2s для FIN; максимум 30s при исчезновении источника. signals: void statusChanged(const QString& status); void framePresented(); protected: void closeEvent(QCloseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; private: struct Playback; std::unique_ptr m_playback; bool m_audioAttached = false; QTimer m_timer; VideoRenderWidget* m_render = nullptr; QLabel* m_status = nullptr; void attachAudio(); void tick(); void decode(); void decodeVideo(); };