Describe multiview tile lifetime, navigation and retry contracts
This commit is contained in:
1 parent
1f0c7483e0
commit
a03abcaa02
2 files changed
+41
-27
No files matched your search
@@ -2,7 +2,9 @@
|
||||
* Плитка сохраняет player/decoder/часы при перекладке; новая эпоха заменяет только соответствующий player.
|
||||
* Своя плитка получает setLocalPreview до кодирования, без reader/audio; клик запрашивает управление.
|
||||
* Чужая плитка разворачивается без пересоздания players; Esc/повторный клик возвращает сетку.
|
||||
* Пропавшему источнику даётся дочитать FIN, затем плитка удаляется. Пустое окно ждёт новые публикации. */
|
||||
* Пропавшему источнику даётся дочитать FIN, затем плитка удаляется. Пустое окно ждёт новые публикации.
|
||||
* Все методы — GUI-поток; диспетчер передаёт снимки через onSources, publisher — кадры через setLocalPreview.
|
||||
* Закрытие терминально: останавливает таймер/плитки и снимает свой watch. WA_DeleteOnClose задаёт владелец окна. */
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QTimer>
|
||||
@@ -21,52 +23,64 @@ class QCloseEvent;
|
||||
class StreamGridWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// group — ненулевая CHAT-группа; parent владеет окном по правилам QWidget, конструктор начинает свой watch.
|
||||
explicit StreamGridWindow(uint64_t group, QWidget* parent = nullptr);
|
||||
// Идемпотентно остановить viewers/watch; уничтожение плиток завершает их workers.
|
||||
~StreamGridWindow() override;
|
||||
// Неизменная группа этого окна.
|
||||
uint64_t group() const { return m_group; }
|
||||
// Заимствованный CHAT_EVT_STREAM_SOURCES: ошибка/другая группа игнорируются; valid=false сохраняет все плитки.
|
||||
// Новая эпоха или смена self пересоздаёт только соответствующую плитку, остальные сохраняют playback.
|
||||
void onSources(const uint8_t* data, int length);
|
||||
void setLocalPreview(uint64_t group, const QVariant& frame); // Последний QVideoFrame локального publisher, GUI-поток.
|
||||
// Последний QVideoFrame локального publisher в QVariant; пустой кадр сбрасывает preview, другая группа игнорируется.
|
||||
void setLocalPreview(uint64_t group, const QVariant& frame);
|
||||
// Число плиток, включая исчезнувшие источники, пока они дочитывают остаток.
|
||||
int tileCount() const { return static_cast<int>(m_tiles.size()); }
|
||||
// Сторона сетки ceil(sqrt(tileCount)); 0 для пустого окна, не меняется при разворачивании одной плитки.
|
||||
int gridSide() const { return m_side; }
|
||||
// ID развёрнутой чужой плитки; 0 означает общий вид.
|
||||
uint64_t focusedSource() const { return m_focused; }
|
||||
signals:
|
||||
void publishRequested(quint64 group); // Открыть управление своей публикацией, сохранив это окно.
|
||||
protected:
|
||||
// stop до штатного закрытия QWidget; повторное show не возобновляет просмотр.
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
// Отложенная активация по короткому клику; drag/колесо остаются у видео для pan/zoom.
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
private:
|
||||
struct Tile {
|
||||
uint64_t source, epoch;
|
||||
QString name;
|
||||
bool self = false, previewPresented = false;
|
||||
uint64_t source, epoch; // Узел источника и эпоха публикации из каталога.
|
||||
QString name; // Имя заголовка/повторно открываемого player.
|
||||
bool self = false, previewPresented = false; // Прямой локальный preview; хотя бы один его кадр уже был показан.
|
||||
QWidget* widget = nullptr; // Владеет player либо прямым preview, удаляется целиком.
|
||||
QWidget* video = nullptr; // Поверхность изображения; сюда направлены клики.
|
||||
StreamPlayerWindow* player = nullptr;
|
||||
StreamPlayerWindow* player = nullptr; // Только remote: владелец decoder, аудиочасов и viewer.
|
||||
QLabel* status = nullptr; // Статус прямого preview; у remote им владеет player.
|
||||
qint64 removeAt = -1, retryAt = 0;
|
||||
unsigned failures = 0;
|
||||
qint64 removeAt = -1, retryAt = 0; // Дедлайны m_clock в мс: -1 не удалять, 0 повтор не назначен.
|
||||
unsigned failures = 0; // Backoff decoder: 500мс, 1/2/4/8с, далее максимум 10с.
|
||||
};
|
||||
uint64_t m_group, m_owner;
|
||||
std::vector<Tile> m_tiles;
|
||||
uint64_t m_group, m_owner; // Группа и независимый process-local owner каталога.
|
||||
std::vector<Tile> m_tiles; // Каждый widget принадлежит окну; удаляется целиком при смене идентичности/stop.
|
||||
// Элементы интерфейса принадлежат QObject-дереву этого окна.
|
||||
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;
|
||||
QTimer m_timer; // 250мс: повтор watch, backoff decoder и завершение исчезнувших источников.
|
||||
QElapsedTimer m_clock; // Общие монотонные часы дедлайнов плиток.
|
||||
int m_side = 0; // Последняя рассчитанная сторона сетки.
|
||||
bool m_watching = false, m_closed = false; // Watch принят bridge; окно терминально остановлено.
|
||||
uint64_t m_focused = 0; // Source развёрнутой чужой плитки; 0 — сетка.
|
||||
Qt::WindowStates m_gridWindowState;
|
||||
QPointer<QObject> 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();
|
||||
Qt::WindowStates m_gridWindowState; // Состояние окна до разворачивания remote-плитки.
|
||||
QPointer<QObject> m_pressed; // Поверхность нажатия; удаление плитки безопасно обнуляет указатель.
|
||||
QPoint m_pressPosition; // Глобальная позиция нажатия для отличия клика от drag.
|
||||
bool m_dragged = false; // Пройден QApplication::startDragDistance.
|
||||
void createTile(Tile& tile); // Создать self-preview либо embedded player, установить фильтры кликов.
|
||||
void updatePreview(Tile& tile); // Передать m_preview поверхности self и обновить её статус.
|
||||
void activateTile(uint64_t source); // Self запрашивает управление; remote разворачивается/возвращается в сетку.
|
||||
void restoreGrid(); // Восстановить состояние окна и раскладку, сохранив playback всех плиток.
|
||||
void arrange(); // Переложить/скрыть widgets, сохранив их decoder, reader и часы.
|
||||
void tick(); // Повторить неудачный watch/decoder либо удалить дочитавшую/просроченную плитку.
|
||||
void stop(); // Терминально остановить таймер/watch и уничтожить плитки; повтор безопасен.
|
||||
};
|
||||
@@ -20,7 +20,7 @@ struct StreamRecord {
|
||||
uint64_t id = 0, pts = 0; // ID viewer и PTS этого источника в микросекундах; PTS значим для media.
|
||||
StreamRecordKind kind = StreamRecordKind::Gap; // Media либо событие открытия/разрыва/завершения трека.
|
||||
stream_media_format format{}; // Формат VIDEO/AUDIO; для управляющих событий нули.
|
||||
int flags = 0, reason = 0; // VIDEO: STREAM_MEDIA_FLAG_KEY; CLOSED: stream_close_reason.
|
||||
int flags = 0, reason = 0; // VIDEO: STREAM_MEDIA_KEY; CLOSED: stream_close_reason.
|
||||
QByteArray data; // Owned access unit/Opus; у управляющих событий пусто, живёт независимо от viewer/ядра.
|
||||
};
|
||||
|
||||
@@ -42,7 +42,7 @@ bool gui_stream_open(uint64_t id, uint64_t group, uint64_t source, uint64_t epoc
|
||||
void (*notify)(void*) = nullptr, void* arg = nullptr);
|
||||
/* Синхронно разбудить/снять notify и асинхронно закрыть viewer; старый id допустим, полученные records сохраняются. */
|
||||
void gui_stream_stop(uint64_t id);
|
||||
/* true — одна готовая запись записана в record. false — record не меняется; при наличии manager запрошена выдача.
|
||||
/* true — одна готовая запись записана в record. false — record не меняется; при отсутствии готового ответа запрашивается выдача.
|
||||
* position<0 прогревает звук до KEY; иначе позиция playback в мкс. audioAhead=-1 отключает звук, иначе 0..60000000 мкс.
|
||||
* Это режим выдачи по одной записи: не смешивать с pull_batch на том же id без сброса reader. */
|
||||
bool gui_stream_pull(uint64_t id, StreamRecord& record, int64_t position = -1,
|
||||
|
||||
Reference in new issue
Block a user