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) каждые 250500 мс.
  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).

Структура репозитория (предложение)

.
├─ 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 в этом репозитории.

S
Description
No description provided
Readme MIT 72 KiB