using Ayay.SerilogLogs; using Serilog; namespace SHH.CameraSdk; /// /// 帧控制器(混合模式最终版) /// 策略: /// 1. 高速直通 (> 20 FPS):直接放行,保留硬件原始流畅度与波动。 /// 2. 低速精控 (<= 20 FPS):使用积分算法进行精准降采样。 /// public class FrameController { private ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core); // 需求字典 private readonly ConcurrentDictionary _requirements = new(); // 积分累加器(仅用于 <= 20 FPS 的精准控制) private readonly ConcurrentDictionary _accumulators = new(); private long _globalSequence = 0; // 逻辑基准分母:假设标准输入是 25 帧 // 用于低帧率时的积分计算基准 private const int LOGICAL_BASE_FPS = 25; // --------------------------------------------------------- // 注册与注销 // --------------------------------------------------------- public void Register(string appId, int fps) { _requirements.AddOrUpdate(appId, _ => new FrameRequirement { AppId = appId, TargetFps = fps }, (_, old) => { old.TargetFps = fps; return old; }); // 重置该用户的积分器,确保新策略从零开始 _accumulators.TryRemove(appId, out _); } // 修改 Register 方法,接收整个 Requirement 对象或多个参数 public void Register(FrameRequirement req) { _requirements.AddOrUpdate(req.AppId, _ => req, // 如果不存在,直接添加整个对象 (_, old) => { // 如果已存在,更新关键业务字段,同时保留统计状态 old.TargetFps = req.TargetFps; old.Memo = req.Memo; old.Handle = req.Handle; old.Type = req.Type; old.SavePath = req.SavePath; // 注意:不要覆盖 old.RealFps,保留之前的统计值 return old; }); // 如果是降频(<=20),确保积分器存在 if (req.TargetFps <= 20) { _accumulators.GetOrAdd(req.AppId, 0); } } public void Unregister(string appId) { if (string.IsNullOrWhiteSpace(appId)) return; // 1. 从需求配置中移除 _requirements.TryRemove(appId, out _); // 2. 从积分累加器中移除(防止内存泄漏) _accumulators.TryRemove(appId, out _); _sysLog.Warning($"[Core] 帧控制器已从调度中心彻底移除 AppId: {appId}"); } // --------------------------------------------------------- // 核心决策逻辑 // --------------------------------------------------------- /// /// 混合决策:高速直通 + 低速精控 /// /// 兼容参数(内部逻辑不强依赖) /// 兼容参数(已忽略,防止震荡) public FrameDecision MakeDecision(long currentTick, int ignoredRealFps = 0) { var decision = new FrameDecision { Sequence = Interlocked.Increment(ref _globalSequence), Timestamp = DateTime.Now }; foreach (var req in _requirements.Values) { if (req.TargetFps <= 0) continue; //// ========================================================= //// 【策略 A】 高速直通区 (> 20 FPS) //// ========================================================= //// 用户想要 21~25+ 帧,或者全速。 //// 此时不做任何干预,相机来多少发多少,保留原始的 24-26 波动。 //if (req.TargetFps > 20) //{ // decision.TargetAppIds.Add(req.AppId); // req.LastCaptureTick = currentTick; // 更新活跃状态 // continue; //} // ========================================================= // 【策略 B】 低速精控区 (<= 20 FPS) -> 积分算法 // ========================================================= // 解决 "16帧" 问题,保证 1帧、5帧、15帧 的绝对精准 // 1. 获取积分 int acc = _accumulators.GetOrAdd(req.AppId, 0); // 2. 累加:每来一帧,积攒 "TargetFps" 分 acc += req.TargetFps; // 3. 判定:是否攒够了 25 分 (逻辑基准) if (acc >= LOGICAL_BASE_FPS) { // 发货 decision.TargetAppIds.Add(req.AppId); // 扣除成本,保留余数 (余数是精度的关键) acc -= LOGICAL_BASE_FPS; // 【核心修复】在此处触发统计,RealFps 才会开始跳动 req.UpdateRealFps(); req.LastCaptureTick = currentTick; } // 4. 防爆桶机制:如果累积太多(例如相机推流极快),限制封顶 // 防止下一秒瞬间吐出太多帧 if (acc > LOGICAL_BASE_FPS) acc = LOGICAL_BASE_FPS; // 5. 写回状态 _accumulators[req.AppId] = acc; } decision.IsCaptured = decision.TargetAppIds.Count > 0; return decision; } // --------------------------------------------------------- // 辅助状态查询 // --------------------------------------------------------- public List GetCurrentRequirements() { return _requirements.Values.Select(r => new { r.AppId, r.TargetFps, r.RealFps, LastActive = r.LastCaptureTick, r.Memo, r.SavePath, r.Handle, r.TargetIp, r.TargetPort, r.Protocol, r.Type }).ToList(); } // [新增] 专门供审计与管理层调用的强类型方法 // Optimized: 避免匿名类型跨程序集访问失败,提供高性能的实体访问 public IEnumerable GetRequirements() { return _requirements.Values; } }