d8e69c6392
User: "при движении объект оборачивается в рамку, но функционал не
учитывает что сетки могут переключаться и координаты ячеек меняются".
Был баг: detbox-overlay хранил cell_x/y/w/h из --detection-cell CLI
(заданы при старте), при смене layout рамки рисовались по старым
координатам — мимо камеры.
Изменения:
- overlay.h/.c: новый API cfc_overlay_detbox_set_cell_geom(ov,x,y,w,h).
Mutex-защищённое обновление detbox config'а — composer вызывает
перед каждым draw.
- CameraCell: добавлено поле source_key (хранит cuframes-key камеры,
рендерящейся в этом cell). Layout::apply передаёт его из pool entry.
- Layout::find_camera_cell_rect(key) — возвращает Rect текущей cell
для камеры с заданным cuframes-key (или nullptr если её нет в layout).
- SourcePool::by_frigate_camera(name) — lookup pool-entry по
Frigate-camera-key (frigate event'ы приходят с этим именем).
- Composer::compose_frame: перед draw каждого DETECTION_BOXES overlay'я
— lookup frigate→cuframes_key→layout cell rect, обновляет detbox geom.
Если камера не в layout сейчас — cell_w/h=0, detbox draw skip'ает.
Теперь bbox от Frigate переезжает за камерой:
- tpl_1 → bbox в full screen 1920×1080
- tpl_3 → bbox в main 1440×810
- tpl_4 → bbox в quad ячейке 960×540
- камера не в layout → bbox скрыт
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
235 lines
13 KiB
C
235 lines
13 KiB
C
/* cuframes-composer — overlay'и (border, png-иконка, текст) поверх
|
||
* композиции.
|
||
*
|
||
* Архитектура:
|
||
* cfc_overlay_t — opaque объект одного overlay'я (border, png, text).
|
||
* composer ведёт список overlays + порядок (z-order); на каждом compose'е
|
||
* проходит по списку и рисует их поверх NV12 grid'а.
|
||
*
|
||
* Типы overlay'ев:
|
||
* BORDER — цветная рамка вокруг rect'а (Phase 3a). Реализован через 4
|
||
* fill_nv12 операции (top/bottom/left/right). Полезно для:
|
||
* - визуальной отбивки ячеек grid'а
|
||
* - подсветки активных/неактивных источников (красная при DEAD)
|
||
* PNG — RGBA-изображение (Phase 3b). Decode-раз через stb_image,
|
||
* upload в VRAM, на каждом кадре alpha-blit через
|
||
* cfc_kern_alpha_blit_rgba_*. Для статичных иконок (NO SIGNAL,
|
||
* offline, recording dot).
|
||
* TEXT — динамический рендер строки (Phase 3c). Требует FreeType +
|
||
* font atlas. Сделать позже когда FreeType добавлен как зависимость.
|
||
*
|
||
* Жизненный цикл:
|
||
* cfc_overlay_create_*() — выделяет ресурсы (для PNG/TEXT — VRAM-атлас).
|
||
* cfc_overlay_update_*() — обновляет параметры (положение, видимость, текст).
|
||
* cfc_overlay_draw() — вызывается composer'ом на каждом кадре.
|
||
* cfc_overlay_destroy() — освобождает VRAM.
|
||
*
|
||
* Phase 3a лимит: только BORDER через fill_nv12.
|
||
*
|
||
* Лицензия: LGPL-2.1+
|
||
*/
|
||
|
||
#ifndef CUFRAMES_COMPOSER_OVERLAY_H
|
||
#define CUFRAMES_COMPOSER_OVERLAY_H
|
||
|
||
#include <cuda.h>
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
typedef enum cfc_overlay_type {
|
||
CFC_OVERLAY_BORDER = 0,
|
||
CFC_OVERLAY_PNG = 1, /* Phase 3b */
|
||
CFC_OVERLAY_TEXT = 2, /* Phase 3c */
|
||
CFC_OVERLAY_DETECTION_BOXES = 3, /* Phase 7 — Frigate-driven rectangles */
|
||
} cfc_overlay_type_t;
|
||
|
||
typedef struct cfc_overlay cfc_overlay_t;
|
||
|
||
/* Назначить overlay'ю короткий ID для lookup через control plane (Phase 4).
|
||
* ID копируется внутрь overlay'я (макс 31 символ). Без ID overlay тоже работает,
|
||
* но управлять им runtime'ом нельзя. Можно вызвать после create. */
|
||
int cfc_overlay_set_id(cfc_overlay_t *ov, const char *id);
|
||
|
||
/* Получить ID overlay'я (NULL если не задан). */
|
||
const char *cfc_overlay_get_id(const cfc_overlay_t *ov);
|
||
|
||
/* Получить тип overlay'я. */
|
||
cfc_overlay_type_t cfc_overlay_get_type(const cfc_overlay_t *ov);
|
||
|
||
/* Параметры BORDER overlay'я. */
|
||
typedef struct cfc_overlay_border_config {
|
||
int x, y, w, h; /* прямоугольник в full-res пикселях */
|
||
int thickness; /* толщина рамки в пикселях */
|
||
int color_y, color_u, color_v; /* цвет в BT.709 (Y:16-235, UV:16-240) */
|
||
int alpha; /* 0..255; 0 = invisible */
|
||
int visible; /* 0/1 — выводить ли вообще */
|
||
} cfc_overlay_border_config_t;
|
||
|
||
/* Создать BORDER overlay. */
|
||
int cfc_overlay_create_border(const cfc_overlay_border_config_t *cfg,
|
||
cfc_overlay_t **out);
|
||
|
||
/* Параметры PNG overlay'я. */
|
||
typedef struct cfc_overlay_png_config {
|
||
const char *path; /* путь к PNG-файлу (декод один раз) */
|
||
int x, y; /* позиция top-left на output буфере (чётные) */
|
||
int extra_alpha; /* 0..255 общий множитель прозрачности */
|
||
int visible; /* 0/1 — выводить ли */
|
||
} cfc_overlay_png_config_t;
|
||
|
||
/* Создать PNG overlay. Декодирует файл через libpng, аллоцирует RGBA-атлас
|
||
* в VRAM (cuMemAlloc), копирует туда декодированные пиксели один раз.
|
||
*
|
||
* Размер изображения берётся из PNG header'а — caller узнаёт width/height
|
||
* через cfc_overlay_png_size после create. */
|
||
int cfc_overlay_create_png(const cfc_overlay_png_config_t *cfg,
|
||
cfc_overlay_t **out);
|
||
|
||
/* Получить реальный размер декодированного PNG. После create — стабильны
|
||
* до destroy. */
|
||
int cfc_overlay_png_size(cfc_overlay_t *ov, int *width, int *height);
|
||
|
||
/* Обновить параметры PNG overlay'я (без re-decode). */
|
||
int cfc_overlay_update_png(cfc_overlay_t *ov,
|
||
const cfc_overlay_png_config_t *cfg);
|
||
|
||
/* Параметры TEXT overlay'я (Phase 3c). */
|
||
typedef struct cfc_overlay_text_config {
|
||
const char *font_path; /* путь к .ttf / .otf */
|
||
const char *text; /* UTF-8 строка для рендера */
|
||
int pixel_size; /* высота glyph'а в пикселях (10..200) */
|
||
int x, y; /* top-left на output буфере (чётные) */
|
||
int r, g, b; /* sRGB цвет 0..255 */
|
||
int extra_alpha; /* 0..255 общий множитель прозрачности */
|
||
int visible; /* 0/1 — выводить ли */
|
||
/* Опциональный полупрозрачный фон (подложка) под текстом.
|
||
* bg_alpha = 0 → без фона (default)
|
||
* bg_alpha > 0 → fill rect (atlas_w + 2*bg_pad) × (atlas_h + 2*bg_pad)
|
||
* с цветом bg_y/u/v перед blit'ом текста.
|
||
* bg_pad чётный, default 8 если bg_alpha>0 и bg_pad==0. */
|
||
int bg_alpha; /* 0..255 (0 = отключено) */
|
||
int bg_y, bg_u, bg_v; /* BT.709 limited (Y=16..235, UV=16..240) */
|
||
int bg_pad; /* px padding вокруг текста */
|
||
} cfc_overlay_text_config_t;
|
||
|
||
/* Создать TEXT overlay. Открывает font через FreeType, рендерит строку
|
||
* в RGBA-атлас на CPU (alpha-channel из glyph bitmap'ов anti-aliased),
|
||
* заливает в VRAM. */
|
||
int cfc_overlay_create_text(const cfc_overlay_text_config_t *cfg,
|
||
cfc_overlay_t **out);
|
||
|
||
/* Обновить TEXT overlay. Если text изменился — re-render atlas (VRAM
|
||
* перевыделяется). font_path и pixel_size менять нельзя — заведите новый
|
||
* overlay (face и связанные ресурсы пере-init'ить дорого). */
|
||
int cfc_overlay_update_text(cfc_overlay_t *ov,
|
||
const cfc_overlay_text_config_t *cfg);
|
||
|
||
/* Получить ширину/высоту текущего рендеренного текста (в пикселях). */
|
||
int cfc_overlay_text_size(cfc_overlay_t *ov, int *width, int *height);
|
||
|
||
/* ── DETECTION_BOXES (Phase 7) ─────────────────────────────────────────
|
||
* Managed-список border'ов для bbox'ов от Frigate. Один overlay соответствует
|
||
* одной композитор-ячейке (одной камере). MQTT-subscriber (отдельный thread)
|
||
* вызывает _upsert при каждом frigate/events update и _end при event завершён.
|
||
* draw_detection_boxes итерирует по active box'ам, проверяет TTL, рисует
|
||
* рамку с координатным mapping'ом detect → cell.
|
||
*
|
||
* Координаты Frigate отдаются в detect-разрешении камеры (640×480 для
|
||
* parking_overview), composer ячейка обычно 960×540 на 1080p output → нужен
|
||
* линейный scale. Mapping вынесен внутрь draw, не кэшируется — layout switch
|
||
* безопасен. */
|
||
typedef struct cfc_overlay_detbox_config {
|
||
/* Какая Frigate-камера → какая ячейка composer'а. */
|
||
const char *camera_key; /* "parking_overview" — для lookup'а */
|
||
int detect_w, detect_h; /* Frigate detect.{width,height} */
|
||
int cell_x, cell_y, cell_w, cell_h; /* куда мапится на output frame */
|
||
|
||
/* Стиль рамки. */
|
||
int thickness; /* px (4-8 рекомендуется) */
|
||
int color_y, color_u, color_v; /* BT.709 limited (Y=16-235, UV=16-240) */
|
||
int alpha; /* 0..255 */
|
||
|
||
/* TTL — независимая страховка от потери MQTT events.
|
||
* Если последний update был > stale_ms назад — рамка пропадает.
|
||
* Рекомендуется 5000-8000 мс (Frigate publish раз в минуту для stationary,
|
||
* меньше при движении). */
|
||
int stale_ms;
|
||
|
||
/* Список разрешённых zones — если задан, MQTT subscriber пропускает
|
||
* события у которых after.current_zones пусто либо не пересекается с
|
||
* этим списком. Это робастный фильтр когда Frigate 0.17 schema не даёт
|
||
* native objects.filters.<obj>.required_zones (см. commit 1726137).
|
||
* NULL или пустой массив → принимать все события. */
|
||
const char *const *required_zones; /* массив строк */
|
||
int required_zones_count;
|
||
} cfc_overlay_detbox_config_t;
|
||
|
||
int cfc_overlay_create_detection_boxes(
|
||
const cfc_overlay_detbox_config_t *cfg,
|
||
cfc_overlay_t **out
|
||
);
|
||
|
||
/* Получить camera_key из overlay'я — для MQTT subscriber'а чтобы найти
|
||
* правильный overlay по incoming event'у. */
|
||
const char *cfc_overlay_detbox_camera_key(cfc_overlay_t *ov);
|
||
|
||
/* Обновить cell-геометрию runtime (при смене layout композитора). Композитор
|
||
* вызывает перед draw каждым detbox-overlay'ем — пересчитывает положение
|
||
* рамки под текущую позицию камеры. */
|
||
int cfc_overlay_detbox_set_cell_geom(cfc_overlay_t *ov,
|
||
int cell_x, int cell_y,
|
||
int cell_w, int cell_h);
|
||
|
||
/* Проверить пересечение current_zones события с required_zones overlay'я.
|
||
* - если required_zones пуст → всегда 1 (filter off)
|
||
* - если current_zones пуст → 0 (объект вне зон)
|
||
* - иначе 1 если хотя бы одна current ∈ required, 0 иначе
|
||
* Использует overlay subscriber для отсева street-флуда (см. commit 1726137). */
|
||
int cfc_overlay_detbox_match_zones(cfc_overlay_t *ov,
|
||
const char *const *current_zones,
|
||
int n);
|
||
|
||
/* Upsert одного active детекта.
|
||
* event_id — идентификатор Frigate event'а (для трекинга/end).
|
||
* label — "car", "person", и т.п. (для будущего цветового кодирования).
|
||
* x1/y1/x2/y2 — bbox в detect-разрешении (raw Frigate coords).
|
||
* frame_time_ms — Frigate frame_time для TTL.
|
||
* Thread-safe (mutex внутри). */
|
||
int cfc_overlay_detbox_upsert(
|
||
cfc_overlay_t *ov,
|
||
const char *event_id,
|
||
const char *label,
|
||
int x1, int y1, int x2, int y2,
|
||
int64_t frame_time_ms
|
||
);
|
||
|
||
/* End event'а — удалить рамку (вызывается при `type == "end"` в Frigate). */
|
||
int cfc_overlay_detbox_end(cfc_overlay_t *ov, const char *event_id);
|
||
|
||
/* Обновить параметры BORDER overlay'я (можно переключить visible,
|
||
* сменить цвет, изменить позицию). Thread-safe? Нет — caller должен сам
|
||
* заботиться о том, чтобы update не пересекался с draw. В рамках одного
|
||
* compose-thread'а это естественно. */
|
||
int cfc_overlay_update_border(cfc_overlay_t *ov,
|
||
const cfc_overlay_border_config_t *cfg);
|
||
|
||
/* Нарисовать overlay поверх NV12 кадра. Вызывается из cfc_composer_compose.
|
||
* Возвращает 0 при успехе. */
|
||
int cfc_overlay_draw(cfc_overlay_t *ov,
|
||
CUstream stream,
|
||
CUdeviceptr dst_y, int pitch_y,
|
||
CUdeviceptr dst_uv, int pitch_uv,
|
||
int frame_w, int frame_h);
|
||
|
||
/* Уничтожить overlay (освободить VRAM если был). */
|
||
int cfc_overlay_destroy(cfc_overlay_t *ov);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* CUFRAMES_COMPOSER_OVERLAY_H */
|