636b70b64c
Phase 4a — control plane через ZMQ REP socket с JSON-командами.
Позволяет операторам менять text overlay'и runtime'ом без рестарта.
Live-validated: команды set_text label-parking → "⚠ ВНИМАНИЕ ⚠"
и set_text label-gate → "Машина у ворот" отрабатывают, текст
обновляется в потоке RTSP без перерывов.
Phase 4b — MQTT health publisher через libmosquitto. Каждые 10с в
composer/<instance>/health публикуется JSON {active,stale,dead,total,
uptime_s} с retain=true. Опционально публикуется HA MQTT discovery
config — четыре сенсора (active/stale/dead/total) появляются в HA
автоматически с expire_after=30s.
Содержимое:
- include/cuframes_composer/overlay.h — cfc_overlay_set_id/get_id/get_type
для lookup'а через control plane.
- include/cuframes_composer/composer.h — cfc_composer_find_overlay(id).
- include/cuframes_composer/control.h — cfc_control_config_t (endpoint +
composer + cuda_ctx для text rebuild в worker thread).
- src/control.c — ZMQ REP socket в фоновом потоке + zmq_poll с 200мс
timeout'ом для проверки stop_flag. JSON-диспатчер для команд ping /
health / set_text / set_visible / list_overlays. cuCtxSetCurrent
на старте worker'а — без этого update_text валится на cuMemAlloc.
- include/cuframes_composer/health.h — cfc_health_config_t (host/port/
user/pass + topic_prefix/instance + interval + publish_discovery).
- src/health.c — mosquitto_connect_async + loop_start + background
thread с publish health каждые N секунд + один разовый publish_discovery
для HA.
- examples/grid_record — флаги --control tcp://0.0.0.0:5599,
--mqtt host[:port], --mqtt-instance NAME, --mqtt-user/--mqtt-pass.
Для text overlay'ев — prefix "id=NAME:" в --text задаёт control-plane ID.
CMakeLists.txt — find_library(zmq), find_library(json-c),
find_library(mosquitto) + linkage всех трёх.
Production TODO: создать отдельного MQTT user'а для composer'а вместо
переиспользования frigate creds (используется только в smoke).
112 lines
5.3 KiB
C
112 lines
5.3 KiB
C
/* cuframes-composer — высокоуровневый композитор N источников в grid.
|
||
*
|
||
* Управляет:
|
||
* - N cfc_source (фоновые подписки на cuframes-publisher'ы)
|
||
* - Layout (cell positions + size)
|
||
* - Output NV12 buffer (cuMemAlloc, переиспользуется)
|
||
* - Композиция на каждом тике: clear background + resize/blit для каждого ACTIVE,
|
||
* fill чёрным для DEAD/STALE.
|
||
*
|
||
* Phase 2 архитектура:
|
||
* Композитор работает синхронно: caller вызывает cfc_composer_compose() →
|
||
* композитор для каждого источника берёт snapshot и пишет в output. После
|
||
* вызова output NV12 buffer готов к encode.
|
||
*
|
||
* Каноническая интеграция:
|
||
* for (;;) {
|
||
* cfc_composer_compose(comp); // grid готов в output buffer
|
||
* cfc_encoder_encode_frame(enc, ...); // → H.264
|
||
* }
|
||
*
|
||
* Лицензия: LGPL-2.1+
|
||
*/
|
||
|
||
#ifndef CUFRAMES_COMPOSER_COMPOSER_H
|
||
#define CUFRAMES_COMPOSER_COMPOSER_H
|
||
|
||
#include "source.h"
|
||
#include "overlay.h"
|
||
|
||
#include <cuda.h>
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* Описание одной ячейки grid'а — где на output буфере рисуется источник.
|
||
* Все координаты в full-res пикселях, должны быть чётными. */
|
||
typedef struct cfc_composer_cell {
|
||
const char *source_key; /* cuframes key, например "cam-parking" */
|
||
int x, y; /* top-left угол на output буфере */
|
||
int w, h; /* размер ячейки */
|
||
} cfc_composer_cell_t;
|
||
|
||
typedef struct cfc_composer_config {
|
||
/* Output буфер (одно фиксированное разрешение для всего grid'а). */
|
||
int width; /* output ширина (например 3840 для 2×2 1080p) */
|
||
int height; /* output высота (например 2160 для 2×2 1080p) */
|
||
|
||
/* Cells конфигурация. Массив указателей на cells (для удобства user'а). */
|
||
const cfc_composer_cell_t *cells; /* array, не копируется — caller держит */
|
||
int num_cells;
|
||
|
||
/* CUDA устройство. */
|
||
int cuda_device; /* индекс, обычно 0 */
|
||
|
||
/* Backgound цвет (BT.709 limited): Y=16/U=128/V=128 = чёрный. */
|
||
int bg_y, bg_u, bg_v;
|
||
|
||
/* Параметры stale/dead для источников. */
|
||
int reconnect_min_ms; /* default 1000 */
|
||
int reconnect_max_ms; /* default 30000 */
|
||
int stale_threshold_ms; /* default 500 */
|
||
int dead_threshold_ms; /* default 5000 */
|
||
} cfc_composer_config_t;
|
||
|
||
typedef struct cfc_composer cfc_composer_t;
|
||
|
||
/* Создать композитор. Выделяет output NV12 буфер, запускает N source thread'ов. */
|
||
int cfc_composer_create(const cfc_composer_config_t *cfg, cfc_composer_t **out);
|
||
|
||
/* Скомпоновать один кадр. Вернёт указатели на output NV12 (Y + UV) и pitch.
|
||
* Указатели действительны до следующего compose. */
|
||
int cfc_composer_compose(
|
||
cfc_composer_t *comp,
|
||
CUdeviceptr *out_y_ptr,
|
||
int *out_pitch_y,
|
||
int *out_width,
|
||
int *out_height
|
||
);
|
||
|
||
/* Зарегистрировать overlay (border/png/text) для отрисовки поверх grid'а.
|
||
* composer takes ownership — на cfc_composer_destroy всё освобождается.
|
||
* Порядок добавления = z-order (последний рисуется поверх). Лимит — 64. */
|
||
int cfc_composer_add_overlay(cfc_composer_t *comp, cfc_overlay_t *ov);
|
||
|
||
/* Найти overlay по ID (если был задан через cfc_overlay_set_id). Возвращает
|
||
* NULL если не найден. Thread-safe — composer держит overlays в массиве,
|
||
* пока add/destroy не пересекаются с lookup'ом — но control plane вызывает
|
||
* это из своего потока, draw — из своего; они оба только читают список,
|
||
* а update полей overlay'я делается через cfc_overlay_update_text и пр.
|
||
* (содержимое overlay'я под mutex'ом не лежит, нужно лочиться вызывающему). */
|
||
cfc_overlay_t *cfc_composer_find_overlay(cfc_composer_t *comp, const char *id);
|
||
|
||
/* Получить layout статистику по источникам — для debug / health-репортов. */
|
||
typedef struct cfc_composer_health {
|
||
int total; /* всего источников */
|
||
int active; /* в состоянии ACTIVE */
|
||
int stale; /* в STALE */
|
||
int dead; /* DEAD/DISCONNECTED/CONNECTING */
|
||
} cfc_composer_health_t;
|
||
int cfc_composer_get_health(cfc_composer_t *comp, cfc_composer_health_t *out);
|
||
|
||
/* Уничтожить композитор. */
|
||
int cfc_composer_destroy(cfc_composer_t *comp);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* CUFRAMES_COMPOSER_COMPOSER_H */
|