Phase 9 #193: runtime layout switching через ZMQ set_layout

10 predefined layouts (single, dual_horizontal, dual_vertical, quad,
main_plus_preview, six_grid, nine_grid, sixteen_grid, panoramic,
main_with_strip) — normalized [0..1] координаты, port из vf_cuda_grid.c
старого FFmpeg patch'а. Применяются к фактическому output разрешению
композитора через cfc_composer_set_layout(name).

Source pool НЕ пересоздаётся: sources привязаны к индексам cells, layout
меняет только геометрию (cell.x/y/w/h). Это даёт zero-disruption switch
без потери накопленного state и без re-subscribe к cuframes publishers.

ZMQ verbs: set_layout / list_layouts / get_layout. CLI: --layout=NAME
перетирает --cell координаты на старте.

Используется ONVIF wrapper'ом (gx/cctv-onvif:0.1) для PTZ presets:
GotoPreset(token) → ZMQ set_layout(token).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 18:14:58 +01:00
parent ac86534769
commit 2d7fc1e640
7 changed files with 280 additions and 1 deletions
+15 -1
View File
@@ -26,6 +26,7 @@
#include "../include/cuframes_composer/writer.h"
#include "../include/cuframes_composer/audio.h"
#include "../include/cuframes_composer/frigate_mqtt.h"
#include "../include/cuframes_composer/layouts.h"
#include <cuda.h>
@@ -126,6 +127,7 @@ int main(int argc, char **argv)
const char *frigate_mqtt_host = NULL;
int frigate_mqtt_port = 1883;
const char *frigate_topic = "frigate/events";
const char *initial_layout = NULL; /* --layout NAME → set_layout после init */
/* --detection-cell key,camera,dx,dy,dw,dh,detect_w,detect_h[,zone1:zone2:...]
* key — символьное имя для логов (например "parking")
* camera — Frigate camera_key для MQTT match'а ("parking_overview")
@@ -166,10 +168,11 @@ int main(int argc, char **argv)
{"frigate-mqtt", required_argument, 0, 'G'}, /* host[:port] */
{"frigate-topic", required_argument, 0, 'T'},
{"detection-cell", required_argument, 0, 'D'},
{"layout", required_argument, 0, 'L'}, /* named layout (quad, single, ...) */
{0, 0, 0, 0},
};
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:", 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:", opts, NULL)) != -1) {
switch (c) {
case 'o': out_path = optarg; break;
case 'c':
@@ -223,6 +226,7 @@ int main(int argc, char **argv)
break;
}
case 'T': frigate_topic = optarg; break;
case 'L': initial_layout = optarg; 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';
@@ -374,6 +378,16 @@ int main(int argc, char **argv)
fprintf(stderr, "[grid_record] composer %dx%d, %d ячеек\n",
out_w, out_h, num_cells);
/* --layout NAME → applies named layout поверх --cell координат. Удобно
* как default для ONVIF PTZ-управляемого composer'а (старт в quad,
* далее set_layout через ZMQ). */
if (initial_layout) {
if (cfc_composer_set_layout(comp, initial_layout) != 0) {
fprintf(stderr, "[grid_record] --layout '%s' unknown\n", initial_layout);
return 1;
}
}
/* TEXT overlays. */
for (int i = 0; i < num_texts; i++) {
cfc_overlay_text_config_t tc = {