118 lines
4.3 KiB
C#
118 lines
4.3 KiB
C#
namespace SHH.CameraSdk;
|
|
|
|
/// <summary>
|
|
/// 帧控制器(混合模式最终版)
|
|
/// 策略:
|
|
/// 1. 高速直通 (> 20 FPS):直接放行,保留硬件原始流畅度与波动。
|
|
/// 2. 低速精控 (<= 20 FPS):使用积分算法进行精准降采样。
|
|
/// </summary>
|
|
public class FrameController
|
|
{
|
|
// 需求字典
|
|
private readonly ConcurrentDictionary<string, FrameRequirement> _requirements = new();
|
|
|
|
// 积分累加器(仅用于 <= 20 FPS 的精准控制)
|
|
private readonly ConcurrentDictionary<string, int> _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 _);
|
|
}
|
|
|
|
public void Unregister(string appId)
|
|
{
|
|
_requirements.TryRemove(appId, out _);
|
|
_accumulators.TryRemove(appId, out _); // 同步清理,防止内存泄漏
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// 核心决策逻辑
|
|
// ---------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// 混合决策:高速直通 + 低速精控
|
|
/// </summary>
|
|
/// <param name="currentTick">兼容参数(内部逻辑不强依赖)</param>
|
|
/// <param name="ignoredRealFps">兼容参数(已忽略,防止震荡)</param>
|
|
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;
|
|
|
|
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<dynamic> GetCurrentRequirements()
|
|
{
|
|
return _requirements.Values.Select(r => new { r.AppId, r.TargetFps, LastActive = r.LastCaptureTick }).ToList<dynamic>();
|
|
}
|
|
} |