新增 Mjpegplayer 用来播放 Web 流
This commit is contained in:
116
SHH.MjpegPlayer/Server/MjpegSessions.cs
Normal file
116
SHH.MjpegPlayer/Server/MjpegSessions.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using Core.WcfProtocol;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace SHH.MjpegPlayer;
|
||||
|
||||
/// <summary>
|
||||
/// 服务器会话集合 (线程安全重构版)
|
||||
/// </summary>
|
||||
public class MjpegSessions
|
||||
{
|
||||
// 核心改变:使用字典建立索引,Key = DeviceId#TypeCode
|
||||
// 这样可以将查找特定摄像头的复杂度从 O(N) 降低到 O(1)
|
||||
private readonly ConcurrentDictionary<string, List<MjpegSession>> _sessionMap
|
||||
= new ConcurrentDictionary<string, List<MjpegSession>>();
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public MjpegSessions()
|
||||
{
|
||||
PrismMsg<UploadImageRequest>.Subscribe(ProcUploadImageRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化后的图片分发逻辑 (解决 O(N) 和 线程安全问题)
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
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<MjpegSessions>(ex.Message, ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加会话
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
public void AddSession(MjpegSession session)
|
||||
{
|
||||
if (session?.Info?.Key == null) return;
|
||||
|
||||
// 使用 GetOrAdd 确保线程安全地获取或创建 List
|
||||
var list = _sessionMap.GetOrAdd(session.Info.Key, _ => new List<MjpegSession>());
|
||||
|
||||
lock (list)
|
||||
{
|
||||
list.Add(session);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除会话
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前所有会话信息的快照 (用于 HTTP API 统计与展示)
|
||||
/// [新增] 此方法替代旧版直接访问 Sessions 列表,防止 HTTP 线程与 MJPEG 线程发生冲突
|
||||
/// </summary>
|
||||
public List<SessionInfo> GetAllSessionInfos()
|
||||
{
|
||||
var result = new List<SessionInfo>();
|
||||
// 遍历字典,线程安全地收集所有 Info
|
||||
foreach (var kvp in _sessionMap)
|
||||
{
|
||||
// 对内部 List 加锁,确保复制过程不被打断
|
||||
lock (kvp.Value)
|
||||
{
|
||||
result.AddRange(kvp.Value.Select(s => s.Info));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user