/* Окно всех активных трансляций одного канала. Собственный catalogue owner не зависит от выбранного чата. * Плитка сохраняет player/decoder/часы при перекладке; новая эпоха заменяет только соответствующий player. * Своя плитка получает setLocalPreview до кодирования, без reader/audio; клик запрашивает управление. * Чужая плитка разворачивается без пересоздания players; Esc/повторный клик возвращает сетку. * Пропавшему источнику даётся дочитать FIN, затем плитка удаляется. Пустое окно ждёт новые публикации. */ #pragma once #include #include #include #include #include #include #include class StreamPlayerWindow; class QGridLayout; class QLabel; class QPushButton; class QCloseEvent; class StreamGridWindow : public QWidget { Q_OBJECT public: explicit StreamGridWindow(uint64_t group, QWidget* parent = nullptr); ~StreamGridWindow() override; uint64_t group() const { return m_group; } void onSources(const uint8_t* data, int length); void setLocalPreview(uint64_t group, const QVariant& frame); // Последний QVideoFrame локального publisher, GUI-поток. int tileCount() const { return static_cast(m_tiles.size()); } int gridSide() const { return m_side; } uint64_t focusedSource() const { return m_focused; } signals: void publishRequested(quint64 group); // Открыть управление своей публикацией, сохранив это окно. protected: void closeEvent(QCloseEvent* event) override; bool eventFilter(QObject* watched, QEvent* event) override; private: struct Tile { uint64_t source, epoch; QString name; bool self = false, previewPresented = false; QWidget* widget = nullptr; // Владеет player либо прямым preview, удаляется целиком. QWidget* video = nullptr; // Поверхность изображения; сюда направлены клики. StreamPlayerWindow* player = nullptr; QLabel* status = nullptr; // Статус прямого preview; у remote им владеет player. qint64 removeAt = -1, retryAt = 0; unsigned failures = 0; }; uint64_t m_group, m_owner; std::vector m_tiles; QGridLayout* m_grid = nullptr; QLabel* m_status = nullptr; QLabel* m_empty = nullptr; QPushButton* m_close = nullptr; QVariant m_preview; // Только последний кадр издателя; сеть/декодер для self не открываются. QTimer m_timer; QElapsedTimer m_clock; int m_side = 0; bool m_watching = false, m_closed = false; uint64_t m_focused = 0; // Source развёрнутой чужой плитки; 0 — сетка. Qt::WindowStates m_gridWindowState; QPointer m_pressed; QPoint m_pressPosition; bool m_dragged = false; void createTile(Tile& tile); void updatePreview(Tile& tile); void activateTile(uint64_t source); void restoreGrid(); void arrange(); void tick(); void stop(); };