Files
cuframes-composer/include/cuframes_composer/composer.h
T
gx f8e27b9e85 Phase 10/11 WIP — pool + motion-mode + 8×8 templates (rolled back)
Объединённое состояние работ:
  - Phase 10: source pool, motion-driven layout, Frigate motion_pulse,
    zone-filter в pool, --source / --motion-mode CLI
  - Phase 11 Steps 1-3c: 8×8 микро-сетка, JSON templates с asymmetric
    layouts (tpl_1/3/4/5/6/7/8), reload через ZMQ, auto-labels per camera

В прод отдеплоено и откачено: используем gx/cuframes-composer:0.10 как
baseline. Phase 11 продолжится в branch phase11b-cpp как C++ refactor
с ООП-моделью Cell/Layout/Decoration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 21:24:23 +01:00

186 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 */
/* Prefix для consumer_name внутри cuframes_subscriber_create.
* default = "composer" (для обратной совместимости с фазами 1-6).
* Уникальный prefix позволяет нескольким composer'ам одновременно
* subscribe к одним и тем же publisher'ам (cfc-grid + cuda-grid-pipeline
* параллельно, у каждого свой namespace). */
const char *consumer_prefix;
} 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 runtime: применяет normalized cells из named layout
* к фактическому output разрешению, пересчитывает comp->cells[].x/y/w/h.
*
* Source pool НЕ пересоздаётся: source threads привязаны к индексам ячеек
* (cell[0]=source[0], cell[1]=source[1], ...). Если у нового layout
* больше ячеек чем sources — лишние cells без источника останутся blackout'нутыми.
* Если у нового layout меньше ячеек чем sources — лишние sources продолжают
* работать, но не draw'аются (Phase 9 acceptable).
*
* Thread-safety: caller (ZMQ control thread) должен гарантировать что
* compose thread не выполняется параллельно. На single-threaded compose
* loop'е (Phase 2/3) это natural: control обработает запрос между кадрами. */
int cfc_composer_set_layout(cfc_composer_t *comp, const char *layout_name);
/* Получить имя текущего layout'а (если был задан через set_layout).
* Возвращает NULL если cells выставлены вручную через --cell. */
const char *cfc_composer_current_layout(cfc_composer_t *comp);
/* ── Motion-driven auto layout (Phase 10) ────────────────────────────────
* Composer держит pool из N (до 32) sources с priority. При включённом
* motion-mode на каждом compose'е выбирает active (last_motion_ms < TTL),
* сортирует по priority DESC, и применяет layout по count активных:
* 0 → top-1 by priority в single
* 1 → single
* 2 → dual_horizontal
* 3-4 → quad
* 5 → main_with_strip
* 6 → six_grid
* 7-9 → nine_grid
* 10-16 → sixteen_grid
* Сигналы motion поступают через cfc_composer_motion_pulse() — обычно из
* Frigate MQTT subscriber'а (frigate_mqtt.c). */
/* Добавить источник в pool (отдельно от --cell). Источник создаётся сразу,
* cuframes subscription стартует. Source доступен через motion-relayout.
*
* required_zones (опц.) — colon-separated whitelist для motion-фильтра:
* только события Frigate с current_zones intersect этот список считаются
* motion. Без него все события от frigate_camera считаются motion
* (полезно если камера без zones либо motion и так осмысленный). */
int cfc_composer_add_pool_source(
cfc_composer_t *comp,
const char *cuframes_key, /* "cam-parking" */
const char *frigate_camera, /* "parking_overview" для motion match */
int priority, /* выше = важнее, top попадает в первую ячейку */
const char *required_zones /* "parking_zone:canopy:private_area" или NULL */
);
/* Включить/выключить motion-mode. ttl_ms — сколько камера остаётся
* активной после последнего события (0 → default 45000). */
int cfc_composer_set_motion_mode(cfc_composer_t *comp, int on, int ttl_ms);
/* Узнать, включён ли motion-mode. */
int cfc_composer_get_motion_mode(cfc_composer_t *comp);
/* Зафиксировать факт motion от Frigate-камеры. Используется Frigate MQTT
* subscriber'ом. Если frigate_camera не найдена в pool — no-op.
*
* current_zones — список зон из after.current_zones события. Если у source
* задан required_zones — будет применён фильтр: motion засчитывается только
* если current_zones intersect required_zones. Если required_zones пуст или
* NULL — фильтр выключен. */
int cfc_composer_motion_pulse(cfc_composer_t *comp,
const char *frigate_camera,
const char *const *current_zones,
int n_zones);
/* Получить 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 */