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>
This commit is contained in:
+78
-7
@@ -128,6 +128,12 @@ int main(int argc, char **argv)
|
||||
int frigate_mqtt_port = 1883;
|
||||
const char *frigate_topic = "frigate/events";
|
||||
const char *initial_layout = NULL; /* --layout NAME → set_layout после init */
|
||||
int motion_mode = 0; /* --motion-mode */
|
||||
int motion_ttl = 45000; /* --motion-ttl ms */
|
||||
/* --source cuframes_key,frigate=camera_name,priority=N[,zones=z1:z2:...] */
|
||||
typedef struct { char key[64], frigate[48], zones[128]; int priority; } source_spec_t;
|
||||
source_spec_t sources[32] = { 0 };
|
||||
int num_sources = 0;
|
||||
/* --detection-cell key,camera,dx,dy,dw,dh,detect_w,detect_h[,zone1:zone2:...]
|
||||
* key — символьное имя для логов (например "parking")
|
||||
* camera — Frigate camera_key для MQTT match'а ("parking_overview")
|
||||
@@ -169,10 +175,15 @@ int main(int argc, char **argv)
|
||||
{"frigate-topic", required_argument, 0, 'T'},
|
||||
{"detection-cell", required_argument, 0, 'D'},
|
||||
{"layout", required_argument, 0, 'L'}, /* named layout (quad, single, ...) */
|
||||
{"source", required_argument, 0, 'S'}, /* pool source: key,frigate=...,priority=N */
|
||||
{"motion-mode", no_argument, 0, 'm'}, /* enable motion-driven auto layout */
|
||||
{"motion-ttl", required_argument, 0, 'k'}, /* TTL ms (default 45000) */
|
||||
{"templates", required_argument, 0, 'z'}, /* path to templates.json */
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
const char *templates_path = NULL;
|
||||
int c;
|
||||
while ((c = getopt_long(argc, argv, "o:c:f:b:W:H:s:r:i:t:C:M:I:U:P:RF:A:G:T:D:L:", opts, NULL)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "o:c:f:b:W:H:s:r:i:t:C:M:I:U:P:RF:A:G:T:D:L:S:mk:z:", opts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'o': out_path = optarg; break;
|
||||
case 'c':
|
||||
@@ -227,6 +238,30 @@ int main(int argc, char **argv)
|
||||
}
|
||||
case 'T': frigate_topic = optarg; break;
|
||||
case 'L': initial_layout = optarg; break;
|
||||
case 'm': motion_mode = 1; break;
|
||||
case 'k': motion_ttl = atoi(optarg); break;
|
||||
case 'z': templates_path = optarg; break;
|
||||
case 'S': {
|
||||
if (num_sources >= 32) {
|
||||
fprintf(stderr, "max 32 sources\n"); return 1;
|
||||
}
|
||||
/* Формат: key[,frigate=name][,priority=N]. */
|
||||
char buf[256]; strncpy(buf, optarg, sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
char *tok = strtok(buf, ",");
|
||||
if (!tok) { fprintf(stderr, "bad --source\n"); return 1; }
|
||||
strncpy(sources[num_sources].key, tok, sizeof(sources[num_sources].key) - 1);
|
||||
while ((tok = strtok(NULL, ",")) != NULL) {
|
||||
if (!strncmp(tok, "frigate=", 8)) {
|
||||
strncpy(sources[num_sources].frigate, tok + 8,
|
||||
sizeof(sources[num_sources].frigate) - 1);
|
||||
} else if (!strncmp(tok, "priority=", 9)) {
|
||||
sources[num_sources].priority = atoi(tok + 9);
|
||||
}
|
||||
}
|
||||
num_sources++;
|
||||
break;
|
||||
}
|
||||
case 'D': {
|
||||
if (num_detcells >= MAX_CELLS) { fprintf(stderr, "max %d detcells\n", MAX_CELLS); return 1; }
|
||||
char buf[512]; strncpy(buf, optarg, sizeof(buf) - 1); buf[sizeof(buf)-1] = '\0';
|
||||
@@ -340,18 +375,39 @@ int main(int argc, char **argv)
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
if (!out_path || num_cells == 0) {
|
||||
if (!out_path || (num_cells == 0 && num_sources == 0)) {
|
||||
fprintf(stderr,
|
||||
"Использование: %s --out <file.h264> --cell key,x,y,w,h [--cell ...]\n"
|
||||
" ИЛИ: %s --out <file.h264> --motion-mode --source ... [--source ...]\n"
|
||||
" [--width 3840] [--height 2160] [--fps 25]\n"
|
||||
" [--bitrate 10000] [--seconds N]\n",
|
||||
argv[0]);
|
||||
" [--bitrate 10000] [--seconds N]\n"
|
||||
" --source cuframes_key[,frigate=name][,priority=N]\n",
|
||||
argv[0], argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Motion-mode + только --source: создаём placeholder cell (motion_relayout
|
||||
* перепишет его перед первым кадром). */
|
||||
if (num_cells == 0 && num_sources > 0) {
|
||||
strncpy(cell_keys[0], sources[0].key, 63);
|
||||
cells[0].source_key = cell_keys[0];
|
||||
cells[0].x = 0; cells[0].y = 0;
|
||||
cells[0].w = out_w; cells[0].h = out_h;
|
||||
num_cells = 1;
|
||||
}
|
||||
|
||||
signal(SIGINT, on_sig);
|
||||
signal(SIGTERM, on_sig);
|
||||
|
||||
/* Загружаем templates.json — если файла нет, остаются built-in. */
|
||||
if (templates_path) {
|
||||
int n = cfc_layout_load_file(templates_path);
|
||||
if (n <= 0) {
|
||||
fprintf(stderr, "[grid_record] templates %s: failed (rc=%d), using built-in\n",
|
||||
templates_path, n);
|
||||
}
|
||||
}
|
||||
|
||||
/* CUDA primary context. */
|
||||
CUresult cr = cuInit(0);
|
||||
if (cr != CUDA_SUCCESS) { fprintf(stderr, "cuInit: %s\n", cu_err(cr)); return 1; }
|
||||
@@ -378,9 +434,22 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "[grid_record] composer %dx%d, %d ячеек\n",
|
||||
out_w, out_h, num_cells);
|
||||
|
||||
/* --source: добавить в pool до motion-mode init. Источники cuframes
|
||||
* стартуют здесь же, до первого compose'а. */
|
||||
for (int i = 0; i < num_sources; i++) {
|
||||
cfc_composer_add_pool_source(comp, sources[i].key,
|
||||
sources[i].frigate[0] ? sources[i].frigate : NULL,
|
||||
sources[i].priority,
|
||||
sources[i].zones[0] ? sources[i].zones : NULL);
|
||||
}
|
||||
if (motion_mode) {
|
||||
cfc_composer_set_motion_mode(comp, 1, motion_ttl);
|
||||
}
|
||||
|
||||
/* --layout NAME → applies named layout поверх --cell координат. Удобно
|
||||
* как default для ONVIF PTZ-управляемого composer'а (старт в quad,
|
||||
* далее set_layout через ZMQ). */
|
||||
* далее set_layout через ZMQ). В motion-mode не работает (relayout
|
||||
* перетирает на каждом кадре). */
|
||||
if (initial_layout) {
|
||||
if (cfc_composer_set_layout(comp, initial_layout) != 0) {
|
||||
fprintf(stderr, "[grid_record] --layout '%s' unknown\n", initial_layout);
|
||||
@@ -477,13 +546,15 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
/* Frigate MQTT subscriber (если задан --frigate-mqtt). */
|
||||
/* Frigate MQTT subscriber: запускаем если есть detection-cells
|
||||
* (overlay'ные bbox'ы) ИЛИ motion-mode (auto-layout drivers). */
|
||||
cfc_frigate_mqtt_t *frigate = NULL;
|
||||
if (frigate_mqtt_host && num_detcells > 0) {
|
||||
if (frigate_mqtt_host && (num_detcells > 0 || motion_mode)) {
|
||||
cfc_frigate_mqtt_config_t fc = {
|
||||
.host = frigate_mqtt_host, .port = frigate_mqtt_port,
|
||||
.username = mqtt_user, .password = mqtt_pass,
|
||||
.topic = frigate_topic,
|
||||
.composer = motion_mode ? comp : NULL, /* pulse'ы только в motion-mode */
|
||||
};
|
||||
if (cfc_frigate_mqtt_create(&fc, &frigate) == 0) {
|
||||
for (int i = 0; i < num_detcells; i++) {
|
||||
|
||||
Reference in New Issue
Block a user