Files
Ayay/SHH.CameraSdk/Core/Scheduling/FrameController.cs

98 lines
4.1 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.
namespace SHH.CameraSdk;
/// <summary>
/// 帧控制器(帧调度核心)
/// 功能:管理订阅者的帧需求,基于需求动态判定每帧的处理命运(保留/丢弃、分发目标)
/// 核心逻辑:采用“并集采样”策略,满足任意订阅者的帧率需求即保留帧,避免重复采样浪费资源
/// </summary>
public class FrameController
{
#region --- (Private Resources & States) ---
/// <summary> 订阅者帧需求集合(线程安全) </summary>
/// <remarks> Key订阅者AppIdValue该订阅者的帧需求配置 </remarks>
private readonly ConcurrentDictionary<string, FrameRequirement> _requirements = new();
/// <summary> 全局决策序列号(原子递增,确保决策唯一标识) </summary>
private long _globalSequence = 0;
#endregion
#region --- (Requirement Management) ---
/// <summary>
/// 注册/更新订阅者的帧需求
/// 功能:新增订阅者需求或更新已有订阅者的目标帧率
/// </summary>
/// <param name="appId">订阅者唯一标识(如 "RemoteClient_01"、"AI_Behavior_Engine"</param>
/// <param name="fps">目标帧率单位fps需大于0否则视为无效需求</param>
public void Register(string appId, int fps)
{
// 新增或更新需求:不存在则创建,存在则更新目标帧率
_requirements.AddOrUpdate(appId,
addValueFactory: _ => new FrameRequirement { AppId = appId, TargetFps = fps },
updateValueFactory: (_, oldRequirement) =>
{
oldRequirement.TargetFps = fps;
return oldRequirement;
});
}
/// <summary>
/// [新增] 注销订阅者的帧需求
/// 功能:移除指定订阅者的配置,该订阅者将不再收到任何分发帧
/// </summary>
/// <param name="appId">订阅者唯一标识</param>
public void Unregister(string appId)
{
// ConcurrentDictionary.TryRemove 是原子的、线程安全的
// out _ 表示我们要丢弃移除出的对象,因为我们只关心移除动作本身
_requirements.TryRemove(appId, out _);
}
#endregion
#region --- (Frame Decision Generation) ---
/// <summary>
/// [热路径] 判定当前物理帧是否需要保留并分发
/// 核心逻辑:并集采样,只要任意订阅者达到采样间隔,就保留该帧并分发至对应订阅者
/// </summary>
/// <param name="currentTick">当前系统时间戳(单位:毫秒,建议使用 Environment.TickCount64</param>
/// <returns>帧决策结果(包含是否保留、分发目标等信息)</returns>
public FrameDecision MakeDecision(long currentTick)
{
// 初始化决策对象,生成唯一序列号与时间戳
var decision = new FrameDecision
{
Sequence = Interlocked.Increment(ref _globalSequence), // 原子递增,线程安全
Timestamp = DateTime.Now
};
// 遍历所有订阅者需求,判定是否需要为该订阅者保留当前帧
foreach (var req in _requirements.Values)
{
// 跳过无效需求目标帧率≤0
if (req.TargetFps <= 0) continue;
// 计算该订阅者的采样间隔毫秒1000ms / 目标帧率
long interval = 1000 / req.TargetFps;
// 判定是否达到采样时间:当前时间 - 上次采样时间 ≥ 采样间隔允许1s内相位对齐自动合并
if (currentTick - req.LastCaptureTick >= interval)
{
// 加入分发目标列表
decision.TargetAppIds.Add(req.AppId);
// 更新该订阅者的上次采样时间,避免重复采样
req.LastCaptureTick = currentTick;
}
}
// 判定是否保留该帧存在分发目标则保留IsCaptured=true否则丢弃
decision.IsCaptured = decision.TargetAppIds.Count > 0;
return decision;
}
#endregion
}