namespace SHH.CameraSdk; /// /// 帧控制器(帧调度核心) /// 功能:管理订阅者的帧需求,基于需求动态判定每帧的处理命运(保留/丢弃、分发目标) /// 核心逻辑:采用“并集采样”策略,满足任意订阅者的帧率需求即保留帧,避免重复采样浪费资源 /// public class FrameController { #region --- 私有资源与状态 (Private Resources & States) --- /// 订阅者帧需求集合(线程安全) /// Key:订阅者AppId,Value:该订阅者的帧需求配置 private readonly ConcurrentDictionary _requirements = new(); /// 全局决策序列号(原子递增,确保决策唯一标识) private long _globalSequence = 0; #endregion #region --- 需求管理 (Requirement Management) --- /// /// 注册/更新订阅者的帧需求 /// 功能:新增订阅者需求或更新已有订阅者的目标帧率 /// /// 订阅者唯一标识(如 "RemoteClient_01"、"AI_Behavior_Engine") /// 目标帧率(单位:fps,需大于0,否则视为无效需求) public void Register(string appId, int fps) { // 新增或更新需求:不存在则创建,存在则更新目标帧率 _requirements.AddOrUpdate(appId, addValueFactory: _ => new FrameRequirement { AppId = appId, TargetFps = fps }, updateValueFactory: (_, oldRequirement) => { oldRequirement.TargetFps = fps; return oldRequirement; }); } #endregion #region --- 帧决策生成 (Frame Decision Generation) --- /// /// [热路径] 判定当前物理帧是否需要保留并分发 /// 核心逻辑:并集采样,只要任意订阅者达到采样间隔,就保留该帧并分发至对应订阅者 /// /// 当前系统时间戳(单位:毫秒,建议使用 Environment.TickCount64) /// 帧决策结果(包含是否保留、分发目标等信息) 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 }