/* Composer — оркестратор Phase 11b. * * Owns: * - SourcePool (cuframes-источники + motion state) * - vector (loaded from JSON или builtins) * - Layout (текущее состояние cells) * - OutputSurface (CudaBuffer для NV12 output) * * Compose loop (по кадру): * 1. select_template_and_active() → (LayoutTemplate*, vector) * по правилам: motion_mode? motion-based best-fit : idle top-1 single * 2. hysteresis: рост сразу, уменьшение — wait shrink_hysteresis_ms * 3. если sig != committed → layout_.apply(template, active, W, H) * 4. compose_clear() → output буфер чёрный * 5. layout_.render(stream, NV12Ref) * * Public API экспортируется через composer_c_api.cpp с extern "C" для * совместимости с control.c, grid_record.c, frigate_mqtt.c. * * Лицензия: LGPL-2.1+ */ #ifndef CUFRAMES_COMPOSER_CPP_COMPOSER_HPP #define CUFRAMES_COMPOSER_CPP_COMPOSER_HPP #include "../overlay.h" /* C API — backward compat для CLI overlays */ #include "cuda_raii.hpp" #include "layout.hpp" #include "source_pool.hpp" #include "template.hpp" #include "types.hpp" #include #include #include #include #include namespace cfc { struct ComposerConfig { int width = 1920; int height = 1080; int cuda_device = 0; int bg_y = 16, bg_u = 128, bg_v = 128; /* Motion-mode параметры. */ int motion_ttl_ms = 45000; int shrink_hysteresis_ms = 3000; /* Templates JSON path (empty → built-in). */ std::string templates_path; }; class Composer { public: explicit Composer(const ComposerConfig& cfg); ~Composer(); Composer(const Composer&) = delete; Composer& operator=(const Composer&) = delete; bool ok() const noexcept { return output_.ok(); } SourcePool& pool() noexcept { return pool_; } const SourcePool& pool() const noexcept { return pool_; } /* Motion mode + relayout policy. */ void set_motion_mode(bool on, int ttl_ms = 0); bool motion_mode() const noexcept { return motion_mode_; } /* Загрузить templates из JSON. Возвращает количество, либо <0. */ int load_templates(const std::string& path); /* Перейти на named template (только если motion_mode == false). */ bool set_layout(const std::string& name); const std::string& current_layout_name() const noexcept { return layout_.name(); } int templates_count() const noexcept { return static_cast(templates_.size()); } const std::vector& templates() const noexcept { return templates_; } /* Overlays — backward compat для grid_record.c CLI (--text/--icon/--border) * и Frigate detection_boxes. Рисуются на выходе compose_frame ПОСЛЕ Layout. * Composer takes ownership — destroy()'ит на ~Composer(). */ int add_overlay(cfc_overlay_t* ov); cfc_overlay_t* find_overlay(const std::string& id) const; /* Health отчёт для C ABI shim. */ struct Health { int total = 0; int active = 0; int stale = 0; int dead = 0; }; Health get_health() const; /* Manual cells — для C API без motion-mode (grid_record --cell без --motion-mode). * Каждый вход {source_key, rect} рендерится CameraCell без template'а. */ void set_manual_cells(const std::vector>& cells); /* Один кадр: relayout (если нужно) + clear + render. * Возвращает NV12Ref на output (ptr действителен до следующего compose). */ NV12Ref compose_frame(); private: /* Selection + hysteresis. */ const LayoutTemplate* pick_best_fit(int need) const; std::vector collect_active() const; void maybe_relayout(); static std::string build_signature(const std::string& tpl_name, const std::vector& active); ComposerConfig cfg_; SourcePool pool_; std::vector templates_; Layout layout_; /* Output NV12 буфер (VMM, zero-copy для NVENC). */ CudaBuffer output_; int pitch_y_ = 0; int pitch_uv_ = 0; cudaStream_t stream_ = nullptr; /* default = 0 */ bool motion_mode_ = false; std::int64_t committed_at_ms_ = 0; std::int64_t pending_first_seen_ms_ = 0; std::string committed_signature_; std::string pending_signature_; /* Manual override (PTZ через set_layout): пока now < manual_override_until_ms_ * motion-mode "заморожен", композитор держит зафиксированный layout. */ std::int64_t manual_override_until_ms_ = 0; int manual_override_duration_ms_ = 60000; /* Backward-compat overlay list (CLI overlays + detbox). */ std::vector overlays_; /* Manual cells — alternative режим без motion-mode (grid_record --cell). */ std::vector> manual_cells_; bool manual_applied_ = false; }; } // namespace cfc #endif /* CUFRAMES_COMPOSER_CPP_COMPOSER_HPP */