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;
}
}