using Core.WcfProtocol;
using System.Collections.Concurrent;
namespace SHH.MjpegPlayer;
///
/// 服务器会话集合 (线程安全重构版)
///
public class MjpegSessions
{
// 核心改变:使用字典建立索引,Key = DeviceId#TypeCode
// 这样可以将查找特定摄像头的复杂度从 O(N) 降低到 O(1)
private readonly ConcurrentDictionary> _sessionMap
= new ConcurrentDictionary>();
///
/// 构造函数
///
public MjpegSessions()
{
PrismMsg.Subscribe(ProcUploadImageRequest);
}
///
/// 优化后的图片分发逻辑 (解决 O(N) 和 线程安全问题)
///
///
public void ProcUploadImageRequest(UploadImageRequest req)
{
try
{
string key = $"{req.Id}#{req.Type}";
// 1. 更新通道信息 (ImageChannels 现已是线程安全的)
var chn = MjpegStatics.ImageChannels.Do(req, key);
// 2. O(1) 快速查找关注该流的会话列表
if (_sessionMap.TryGetValue(key, out var targetSessions))
{
// 必须加锁,防止遍历时 List 被其他线程(如 AddSession/RemoveSession) 修改
lock (targetSessions)
{
// 倒序遍历,方便在需要时移除失效会话
for (var i = targetSessions.Count - 1; i >= 0; i--)
{
var session = targetSessions[i];
session.DoImageProc(req);
}
}
if (chn != null) chn.IsPlaying = targetSessions.Count > 0;
}
else
{
if (chn != null) chn.IsPlaying = false;
}
}
catch (Exception ex)
{
//Logs.LogWarning(ex.Message, ex.StackTrace);
}
}
///
/// 添加会话
///
///
public void AddSession(MjpegSession session)
{
if (session?.Info?.Key == null) return;
// 使用 GetOrAdd 确保线程安全地获取或创建 List
var list = _sessionMap.GetOrAdd(session.Info.Key, _ => new List());
lock (list)
{
list.Add(session);
}
}
///
/// 移除会话
///
///
public void RemoveSession(MjpegSession session)
{
if (session?.Info?.Key == null) return;
if (_sessionMap.TryGetValue(session.Info.Key, out var list))
{
lock (list)
{
list.Remove(session);
}
}
}
///
/// 获取当前所有会话信息的快照 (用于 HTTP API 统计与展示)
/// [新增] 此方法替代旧版直接访问 Sessions 列表,防止 HTTP 线程与 MJPEG 线程发生冲突
///
public List GetAllSessionInfos()
{
var result = new List();
// 遍历字典,线程安全地收集所有 Info
foreach (var kvp in _sessionMap)
{
// 对内部 List 加锁,确保复制过程不被打断
lock (kvp.Value)
{
result.AddRange(kvp.Value.Select(s => s.Info));
}
}
return result;
}
}