新增 Mjpegplayer 用来播放 Web 流

This commit is contained in:
2026-01-21 19:03:59 +08:00
parent f79cb6e74d
commit c438edfa0d
71 changed files with 4538 additions and 452 deletions

View 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;
}
}