Files
cuframes-composer/include/cuframes_composer/cpp/composer.hpp
T
gx 858fe61b56 Phase 11b: hybrid PTZ — set_layout с timed motion freeze
User: "PTZ снова не переключает сетки". Причина: при motion-mode set_layout
игнорировался. Теперь: применяется + замораживает motion-mode на
manual_override_duration_ms_ (60s default). По истечении — auto-возврат
в motion-mode.

В Composer добавлено:
  - manual_override_until_ms_ (моноклоk монотонное время)
  - manual_override_duration_ms_ (default 60s)
  - set_layout: применяет template, ставит override timestamp
  - maybe_relayout: пока now < override → пропускаем (sustain manual layout),
    после → лог "expired, возврат в motion-mode" + force relayout

ONVIF server.py одновременно обновлён под актуальные template имена:
  - PTZ_PRESETS: tpl_1 / tpl_4 / tpl_9 / tpl_16 (вместо single/quad/...)
  - ContinuousMove zoom-in → tpl_1, zoom-out → tpl_16,
    pan/tilt → cycle через эти 4

Production smoke:
  GotoPreset tpl_4 → composer log "manual override 'tpl_4' до +60000ms" PASS.

Refs: #195.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 09:24:40 +01:00

153 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Composer — оркестратор Phase 11b.
*
* Owns:
* - SourcePool (cuframes-источники + motion state)
* - vector<LayoutTemplate> (loaded from JSON или builtins)
* - Layout (текущее состояние cells)
* - OutputSurface (CudaBuffer для NV12 output)
*
* Compose loop (по кадру):
* 1. select_template_and_active() → (LayoutTemplate*, vector<PoolEntry*>)
* по правилам: 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 <cuda_runtime.h>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
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<int>(templates_.size());
}
const std::vector<LayoutTemplate>& 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<std::pair<std::string, Rect>>& cells);
/* Один кадр: relayout (если нужно) + clear + render.
* Возвращает NV12Ref на output (ptr действителен до следующего compose). */
NV12Ref compose_frame();
private:
/* Selection + hysteresis. */
const LayoutTemplate* pick_best_fit(int need) const;
std::vector<PoolEntry*> collect_active() const;
void maybe_relayout();
static std::string build_signature(const std::string& tpl_name,
const std::vector<PoolEntry*>& active);
ComposerConfig cfg_;
SourcePool pool_;
std::vector<LayoutTemplate> 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<cfc_overlay_t*> overlays_;
/* Manual cells — alternative режим без motion-mode (grid_record --cell). */
std::vector<std::pair<std::string, Rect>> manual_cells_;
bool manual_applied_ = false;
};
} // namespace cfc
#endif /* CUFRAMES_COMPOSER_CPP_COMPOSER_HPP */