mirror of
https://github.com/deadcxap/P2S_OSD.git
synced 2026-07-02 05:43:41 +03:00
237 lines
9.7 KiB
Markdown
237 lines
9.7 KiB
Markdown
# P2S_OSD
|
||
|
||
Проект наложения телеметрии печати Bambu Lab P2S поверх RTSP-видеопотока камеры принтера.
|
||
|
||
## Цель
|
||
|
||
Получить единый выходной видеопоток (для OBS/go2rtc/других клиентов), в котором уже «вклеены»:
|
||
|
||
- состояние принтера;
|
||
- данные задания печати;
|
||
- текущий слой и прогресс;
|
||
- параметры AMS (катушка, тип, цвет, слот);
|
||
- температуры (сопло, стол, камера);
|
||
- скорости вентиляторов и иные доступные метрики.
|
||
|
||
Ограничения:
|
||
|
||
- принтер в режиме `LAN-only` + включённый `Developer Mode`;
|
||
- взаимодействие по MQTT (как в OpenBambuAPI);
|
||
- входное видео по `rtsps://...:322/streaming/live/1`;
|
||
- работа **без GPU** (CPU-only);
|
||
- один Docker-контейнер;
|
||
- автозапуск без ручных команд после старта контейнера;
|
||
- читаемые логи;
|
||
- устойчивость при пропаже MQTT/RTSP;
|
||
- endpoint healthcheck для мониторинга.
|
||
|
||
---
|
||
|
||
## Вариант 1 (рекомендуется): Python + GStreamer + Cairo/Pango OSD
|
||
|
||
### Идея
|
||
|
||
Собрать сервис на Python, который:
|
||
|
||
1. читает RTSP через GStreamer;
|
||
2. рисует OSD-слой на кадре в пайплайне (`cairooverlay`/`textoverlay`);
|
||
3. держит отдельный MQTT-клиент для статуса принтера;
|
||
4. публикует выход как RTSP (через встроенный `gst-rtsp-server`) или SRT/MPEG-TS;
|
||
5. поднимает HTTP endpoint `/healthz` и `/metrics`.
|
||
|
||
### Инструменты
|
||
|
||
- `python:3.12-slim`;
|
||
- `gstreamer1.0` + `-plugins-base/-good/-bad/-ugly`;
|
||
- Python-библиотеки: `paho-mqtt`, `fastapi`/`aiohttp`, `uvicorn`, `pydantic`;
|
||
- `gst-rtsp-server` (через C-плагин в runtime, либо запуск отдельного процесса `gst-rtsp-launch`, но внутри того же контейнера).
|
||
|
||
### Плюсы
|
||
|
||
- нативный real-time видеопайплайн;
|
||
- хорошая отказоустойчивость за счёт рестартуемых веток пайплайна;
|
||
- OSD рендерится без полного декод/энкод в Python-коде;
|
||
- удобная интеграция метрик и health endpoint.
|
||
|
||
### Минусы
|
||
|
||
- сложнее в настройке GStreamer;
|
||
- нужно аккуратно подобрать кодек/битрейт CPU-only.
|
||
|
||
### План реализации
|
||
|
||
1. **Каркас приложения**
|
||
- `app/main.py` (оркестратор), `app/config.py`, `app/logging.py`.
|
||
2. **MQTT-адаптер**
|
||
- подключение к топикам Bambu;
|
||
- нормализация в единый `PrinterState`.
|
||
3. **State Store**
|
||
- thread-safe/async-safe store;
|
||
- TTL-поля (чтобы помечать устаревшие данные как `N/A`).
|
||
4. **Видеопайплайн**
|
||
- RTSP ingest → decode → overlay → encode (`x264enc ultrafast`) → RTSP output.
|
||
5. **OSD layout**
|
||
- несколько блоков: job, printer, temps, fans, AMS;
|
||
- fallback-текст при отсутствии данных.
|
||
6. **Resilience**
|
||
- retry с backoff для MQTT и RTSP;
|
||
- watchdog пайплайна, автоматический re-init.
|
||
7. **Observability**
|
||
- структурные логи JSON/текст;
|
||
- `/healthz` (liveness/readiness), `/metrics`.
|
||
8. **Docker**
|
||
- `ENTRYPOINT` запускает единый процесс-супервизор.
|
||
|
||
---
|
||
|
||
## Вариант 2: Go + FFmpeg filtergraph (`drawtext`/`overlay`) + MQTT
|
||
|
||
### Идея
|
||
|
||
Go-сервис получает MQTT-статус, генерирует текстовый OSD (или bitmap), а FFmpeg-процесс выполняет транскод и наложение через фильтры.
|
||
|
||
### Инструменты
|
||
|
||
- Go 1.23+;
|
||
- `ffmpeg` (libx264, drawtext);
|
||
- MQTT-клиент (`eclipse/paho.mqtt.golang`);
|
||
- HTTP сервер (`chi`/`net/http`) для health.
|
||
|
||
### Плюсы
|
||
|
||
- проще найти специалистов по FFmpeg;
|
||
- один стабильный бинарь-оркестратор;
|
||
- предсказуемое поведение в Docker.
|
||
|
||
### Минусы
|
||
|
||
- `drawtext` обновляется через перезапись textfile/zmq-команды, что усложняет динамику;
|
||
- сложнее сделать богатый UI (цветные блоки, иконки) без дополнительных костылей.
|
||
|
||
### План реализации
|
||
|
||
1. Go-daemon с подсистемами `mqtt`, `state`, `ffmpeg`, `health`.
|
||
2. Генерация `osd.txt` (atomic write) каждые 250–500 мс.
|
||
3. Запуск FFmpeg с `-vf drawtext=textfile=...:reload=1`.
|
||
4. Выходной поток: RTSP push в локальный go2rtc (внутри контейнера) или прямой RTMP/SRT.
|
||
5. Контроль процесса FFmpeg (перезапуск + throttling).
|
||
6. Prometheus/health endpoints.
|
||
|
||
---
|
||
|
||
## Вариант 3: Встроенный go2rtc + sidecar-процесс OSD внутри одного контейнера
|
||
|
||
### Идея
|
||
|
||
В одном контейнере работают:
|
||
|
||
- `go2rtc` как универсальный ретранслятор;
|
||
- отдельный OSD-процесс (Python/Go), который берёт исходный поток и публикует «обогащённый» поток обратно в go2rtc.
|
||
|
||
> Несмотря на два процесса, это **один контейнер**, что соответствует требованию.
|
||
|
||
### Плюсы
|
||
|
||
- сразу готовая интеграция с экосистемой go2rtc;
|
||
- удобно раздавать в разные протоколы (WebRTC/RTSP/HLS).
|
||
|
||
### Минусы
|
||
|
||
- нужно корректно организовать процесс-менеджмент (s6/supervisord/tini + watchdog);
|
||
- сложнее отладка при циклической маршрутизации потоков.
|
||
|
||
### План реализации
|
||
|
||
1. Собрать image с `go2rtc` + OSD service.
|
||
2. Статический `go2rtc.yaml` с входным потоком принтера и выходом `stream_osd`.
|
||
3. OSD service публикует в локальный ingest (`rtsp://127.0.0.1:8554/...`).
|
||
4. Единый `/healthz` агрегирует состояние обоих процессов.
|
||
5. Логи обоих процессов в stdout/stderr контейнера.
|
||
|
||
---
|
||
|
||
## Рекомендуемая архитектура MVP
|
||
|
||
Для первого релиза: **Вариант 1**.
|
||
|
||
Почему:
|
||
|
||
- лучший баланс между гибкостью OSD и стабильностью в real-time;
|
||
- можно начать с простого текстового overlay и постепенно добавить графические элементы;
|
||
- проще контролировать reconnect-логику в одном коде.
|
||
|
||
### MVP scope (итерация 1)
|
||
|
||
- вход RTSP;
|
||
- MQTT-подписка на базовые поля (status/progress/layers/temps);
|
||
- простое OSD (2 колонки);
|
||
- выход RTSP;
|
||
- `/healthz` и `/readyz`;
|
||
- Docker + `HEALTHCHECK`;
|
||
- устойчивый reconnect.
|
||
|
||
### Итерция 2
|
||
|
||
- AMS-детализация (тип/цвет/слот);
|
||
- красивые плашки/цвета;
|
||
- экспорт метрик Prometheus;
|
||
- профили качества (low/medium/high CPU).
|
||
|
||
---
|
||
|
||
## Структура репозитория (предложение)
|
||
|
||
```text
|
||
.
|
||
├─ app/
|
||
│ ├─ main.py
|
||
│ ├─ config.py
|
||
│ ├─ logging.py
|
||
│ ├─ state/
|
||
│ │ ├─ models.py
|
||
│ │ └─ store.py
|
||
│ ├─ mqtt/
|
||
│ │ ├─ client.py
|
||
│ │ └─ parser.py
|
||
│ ├─ video/
|
||
│ │ ├─ pipeline.py
|
||
│ │ └─ overlay.py
|
||
│ └─ api/
|
||
│ └─ health.py
|
||
├─ Dockerfile
|
||
├─ docker-compose.example.yml
|
||
├─ .env.example
|
||
└─ README.md
|
||
```
|
||
|
||
---
|
||
|
||
## Ключевые нефункциональные требования и как их закрыть
|
||
|
||
1. **Не падать при отсутствии коннекта**
|
||
- бесконечный reconnect c exponential backoff;
|
||
- «деградированный режим» (OSD показывает `NO MQTT` / `NO VIDEO`).
|
||
2. **Читаемые логи**
|
||
- уровни `INFO/WARN/ERROR`, correlation-id сессии подключения;
|
||
- отдельные события `mqtt_connected`, `rtsp_disconnected`, `pipeline_restarted`.
|
||
3. **Автозапуск**
|
||
- `ENTRYPOINT ["python", "-m", "app.main"]`;
|
||
- никаких ручных post-start команд.
|
||
4. **Проверка работоспособности**
|
||
- `/healthz` (жив ли процесс);
|
||
- `/readyz` (есть ли свежие state/video за N сек).
|
||
5. **CPU-only**
|
||
- `x264enc tune=zerolatency speed-preset=ultrafast`;
|
||
- ограничить fps/разрешение в конфиге.
|
||
|
||
---
|
||
|
||
## Что подготовить перед кодированием
|
||
|
||
- список точных MQTT topic/payload полей (по OpenBambuAPI и фактическим payload вашего принтера);
|
||
- целевой формат выходного потока (RTSP/RTMP/WebRTC);
|
||
- допустимая задержка (например, ≤ 2–3 с);
|
||
- CPU-бюджет хоста (число vCPU).
|
||
|
||
После этого можно переходить к реализации MVP в этом репозитории.
|