/* composer_c_api — extern "C" ABI shim для C++ Composer (Phase 11b). * * Существующие callers (control.c, frigate_mqtt.c, examples/grid_record.c) * продолжают использовать prototype cfc_composer_* без изменений. Здесь * каждый из них транслируется в вызов соответствующего метода cfc::Composer. * * Opaque handle cfc_composer_t = cfc::Composer (через reinterpret_cast). * * Лицензия: LGPL-2.1+ */ #include "../../include/cuframes_composer/composer.h" #include "../../include/cuframes_composer/cpp/composer.hpp" #include #include #include #include #include namespace { inline cfc::Composer* as_cpp(cfc_composer_t* h) { return reinterpret_cast(h); } inline cfc_composer_t* as_c(cfc::Composer* c) { return reinterpret_cast(c); } } // namespace extern "C" { int cfc_composer_create(const cfc_composer_config_t* cfg, cfc_composer_t** out) { if (!cfg || !out) return -1; if (cfg->width <= 0 || cfg->height <= 0) return -1; cfc::ComposerConfig cpp_cfg; cpp_cfg.width = cfg->width; cpp_cfg.height = cfg->height; cpp_cfg.cuda_device = cfg->cuda_device; if (cfg->bg_y) cpp_cfg.bg_y = cfg->bg_y; if (cfg->bg_u) cpp_cfg.bg_u = cfg->bg_u; if (cfg->bg_v) cpp_cfg.bg_v = cfg->bg_v; auto* comp = new (std::nothrow) cfc::Composer(cpp_cfg); if (!comp || !comp->ok()) { delete comp; return -1; } /* Если caller передал cells через --cell → запоминаем как manual cells. * Apply отложен до compose_frame (тогда pool уже наполнен через * add_pool_source). */ if (cfg->cells && cfg->num_cells > 0) { std::vector> manual; for (int i = 0; i < cfg->num_cells; i++) { const auto& c = cfg->cells[i]; if (!c.source_key) continue; cfc::Rect r; r.x = c.x; r.y = c.y; r.w = c.w; r.h = c.h; manual.emplace_back(std::string(c.source_key), r); } comp->set_manual_cells(manual); /* Также добавляем источник в pool автоматически — иначе lookup * не найдёт его. Priority=0, frigate=none, zones=[]. */ cfc::SourcePool::SubscribeOpts opts; if (cfg->consumer_prefix && *cfg->consumer_prefix) opts.consumer_prefix = cfg->consumer_prefix; if (cfg->reconnect_min_ms) opts.reconnect_min_ms = cfg->reconnect_min_ms; if (cfg->reconnect_max_ms) opts.reconnect_max_ms = cfg->reconnect_max_ms; if (cfg->stale_threshold_ms) opts.stale_threshold_ms = cfg->stale_threshold_ms; if (cfg->dead_threshold_ms) opts.dead_threshold_ms = cfg->dead_threshold_ms; for (const auto& kv : manual) { comp->pool().add(kv.first, "", 0, {}, opts); } } std::fprintf(stderr, "[cfc/composer] C++ ABI shim, %dx%d, %d manual cells\n", cfg->width, cfg->height, cfg->num_cells); *out = as_c(comp); return 0; } int cfc_composer_compose(cfc_composer_t* h, CUdeviceptr* out_y_ptr, int* out_pitch_y, int* out_width, int* out_height) { if (!h) return -1; cfc::NV12Ref ref = as_cpp(h)->compose_frame(); if (out_y_ptr) *out_y_ptr = ref.y_ptr; if (out_pitch_y) *out_pitch_y = ref.pitch_y; if (out_width) *out_width = ref.frame_w; if (out_height) *out_height = ref.frame_h; return 0; } int cfc_composer_add_overlay(cfc_composer_t* h, cfc_overlay_t* ov) { if (!h) return -1; return as_cpp(h)->add_overlay(ov); } cfc_overlay_t* cfc_composer_find_overlay(cfc_composer_t* h, const char* id) { if (!h || !id) return nullptr; return as_cpp(h)->find_overlay(id); } int cfc_composer_set_layout(cfc_composer_t* h, const char* layout_name) { if (!h || !layout_name) return -1; return as_cpp(h)->set_layout(layout_name) ? 0 : -1; } const char* cfc_composer_current_layout(cfc_composer_t* h) { if (!h) return nullptr; const std::string& n = as_cpp(h)->current_layout_name(); return n.empty() ? nullptr : n.c_str(); } int cfc_composer_add_pool_source(cfc_composer_t* h, const char* cuframes_key, const char* frigate_camera, int priority, const char* required_zones) { if (!h || !cuframes_key) return -1; std::vector zones; if (required_zones && *required_zones) { std::string cur; for (const char* p = required_zones; *p; p++) { if (*p == ':') { if (!cur.empty()) zones.push_back(cur); cur.clear(); } else cur.push_back(*p); } if (!cur.empty()) zones.push_back(cur); } cfc::SourcePool::SubscribeOpts opts; int idx = as_cpp(h)->pool().add(cuframes_key, frigate_camera ? frigate_camera : "", priority, zones, opts); std::fprintf(stderr, "[cfc/composer] pool+ '%s' (frigate=%s prio=%d zones=%zu) → idx=%d\n", cuframes_key, frigate_camera ? frigate_camera : "-", priority, zones.size(), idx); return idx >= 0 ? 0 : -1; } int cfc_composer_set_motion_mode(cfc_composer_t* h, int on, int ttl_ms) { if (!h) return -1; as_cpp(h)->set_motion_mode(on != 0, ttl_ms); return 0; } int cfc_composer_get_motion_mode(cfc_composer_t* h) { return h ? (as_cpp(h)->motion_mode() ? 1 : 0) : 0; } int cfc_composer_motion_pulse(cfc_composer_t* h, const char* frigate_camera, const char* const* current_zones, int n_zones) { if (!h || !frigate_camera) return -1; std::vector zones; for (int i = 0; i < n_zones; i++) { if (current_zones[i]) zones.emplace_back(current_zones[i]); } as_cpp(h)->pool().motion_pulse(frigate_camera, zones); return 0; } int cfc_composer_get_health(cfc_composer_t* h, cfc_composer_health_t* out) { if (!h || !out) return -1; auto hh = as_cpp(h)->get_health(); out->total = hh.total; out->active = hh.active; out->stale = hh.stale; out->dead = hh.dead; return 0; } int cfc_composer_destroy(cfc_composer_t* h) { if (h) delete as_cpp(h); return 0; } } // extern "C"