修复 Bug
This commit is contained in:
@@ -276,12 +276,28 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
// B. 在线应用策略
|
||||
if (device.IsActived)
|
||||
{
|
||||
var options = new DynamicStreamOptions
|
||||
// Optimized: 仅构造真正发生变化的参数
|
||||
var options = new DynamicStreamOptions();
|
||||
|
||||
// 判定码流是否真的变了(或者 DTO 明确传了新值)
|
||||
if (dto.StreamType.HasValue && dto.StreamType != oldConfig.StreamType)
|
||||
{
|
||||
StreamType = dto.StreamType ?? newConfig.StreamType,
|
||||
RenderHandle = (IntPtr)dto.RenderHandle
|
||||
};
|
||||
device.ApplyOptions(options);
|
||||
options.StreamType = dto.StreamType.Value;
|
||||
}
|
||||
|
||||
// 判定句柄是否真的变了
|
||||
// Modified: 只有当 DTO 的句柄与旧配置不一致时,才放入 options
|
||||
if (dto.RenderHandle != oldConfig.RenderHandle)
|
||||
{
|
||||
options.RenderHandle = (IntPtr)dto.RenderHandle;
|
||||
}
|
||||
|
||||
// 只有当至少有一个参数需要更新时,才调用底层
|
||||
// 假设 DynamicStreamOptions 内部有检测是否有值的方法,或者判断其属性
|
||||
if (options.StreamType.HasValue || options.RenderHandle.HasValue)
|
||||
{
|
||||
device.ApplyOptions(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,12 @@ public class SmartFrame : IDisposable
|
||||
/// </summary>
|
||||
public void Activate()
|
||||
{
|
||||
// Optimized: [原因] 使用 Exchange 强制重置归还标记,确保该帧在逻辑上完全从池中脱离,防止归还竞态
|
||||
Interlocked.Exchange(ref _isReturned, 0);
|
||||
|
||||
// 激活后引用计数设为 1,代表生产者(驱动/管道)持有该帧
|
||||
_refCount = 1;
|
||||
_isReturned = 0; // 激活时重置归还标记
|
||||
|
||||
// 记录帧被取出池的时间,用于后续延迟计算
|
||||
Timestamp = DateTime.Now;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ayay.SerilogLogs;
|
||||
using Serilog;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
@@ -94,11 +95,19 @@ public class ProcessingPipeline
|
||||
// 异步遍历队列:收到取消信号时退出循环
|
||||
await foreach (var task in _queue.Reader.ReadAllAsync(_cts.Token))
|
||||
{
|
||||
// 使用 using 语句:处理完成后自动调用 Frame.Dispose(),引用计数-1
|
||||
using (task.Frame)
|
||||
try
|
||||
{
|
||||
// 执行具体的帧处理逻辑
|
||||
ExecuteProcessing(task);
|
||||
// 使用 using 语句:处理完成后自动调用 Frame.Dispose(),引用计数-1
|
||||
using (task.Frame)
|
||||
{
|
||||
// 执行具体的帧处理逻辑
|
||||
ExecuteProcessing(task);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Optimized: [原因] 捕获任务级的异常,防止单帧处理失败导致整个后台处理循环终止
|
||||
_sysLog.Error(ex, "[Pipeline] 关键任务执行异常 (DeviceId: {DeviceId})", task.DeviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ public class CameraCoordinator
|
||||
private readonly SemaphoreSlim _concurrencyLimiter = new(8);
|
||||
|
||||
/// <summary> 相机流存活判定阈值(秒):超过该时间无帧则判定为流中断 </summary>
|
||||
private const int StreamAliveThresholdSeconds = 5;
|
||||
/// 海康 SDK 建立连接+首帧到达通常需 4-8 秒。阈值低了会导致刚连上就被误判为僵死而强制断开。
|
||||
private const int StreamAliveThresholdSeconds = 15;
|
||||
|
||||
/// <summary> Ping 探测超时时间(毫秒) </summary>
|
||||
private const int PingTimeoutMs = 800;
|
||||
@@ -162,7 +163,7 @@ public class CameraCoordinator
|
||||
|
||||
double secondsSinceLastFrame = (nowTick - cam.LastFrameTick) / 1000.0;
|
||||
|
||||
// 2. 判定流是否正常:设备在线 + 5秒内有帧
|
||||
// 2. 判定流是否正常:设备在线 + 15秒内有帧
|
||||
bool isFlowing = cam.IsActived && secondsSinceLastFrame < StreamAliveThresholdSeconds;
|
||||
|
||||
// 3. 判定物理连接是否正常:流正常则直接判定在线;否则执行 Ping+TCP 探测
|
||||
@@ -185,6 +186,7 @@ public class CameraCoordinator
|
||||
// 双重校验:防止等待锁期间状态已变更
|
||||
if (!cam.IsActived)
|
||||
{
|
||||
// 记录启动时刻,elapsed 将重新计时
|
||||
cam.MarkStartAttempt();
|
||||
|
||||
try
|
||||
@@ -224,8 +226,20 @@ public class CameraCoordinator
|
||||
// 【关键修复】:增加了 && cam.IsRunning 判定,防止待机状态下被误复位
|
||||
else if (isPhysicalOk && cam.IsActived && !isFlowing && cam.IsRunning) // [cite: 504]
|
||||
{
|
||||
_sysLog.Warning($"[Coordinator] [自愈] 设备 {cam.Id} 僵死({secondsSinceLastFrame:F1}秒无帧),复位中...");
|
||||
await cam.StopAsync().ConfigureAwait(false);
|
||||
// Optimized: [修复无限重启] 增加“启动保护期”检查。
|
||||
// 原问题:相机刚 StartAsync 还在握手(例如第3秒),isFlowing 为 false,会导致立即被 Stop。
|
||||
// 新逻辑:只有当“启动已超过 15秒”且“依然没流”时,才判定为真正的僵死。
|
||||
|
||||
// elapsed 是毫秒,StreamAliveThresholdSeconds 是秒,需要换算
|
||||
if (elapsed > StreamAliveThresholdSeconds * 1000)
|
||||
{
|
||||
_sysLog.Warning($"[Coordinator] [自愈] 设备 {cam.Id} 僵死({secondsSinceLastFrame:F1}秒无帧),复位中...");
|
||||
await cam.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sysLog.Debug($"[Coordinator] 设备 {cam.Id} 启动握手中 ({elapsed}ms),等待出流...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,4 +154,11 @@ public class FrameController
|
||||
{
|
||||
return _requirements.Values.Select(r => new { r.AppId, r.TargetFps, r.RealFps, LastActive = r.LastCaptureTick, r.Memo, r.SavePath, r.Handle, r.TargetIp, r.TargetPort, r.Protocol, r.Type }).ToList<dynamic>();
|
||||
}
|
||||
|
||||
// [新增] 专门供审计与管理层调用的强类型方法
|
||||
// Optimized: 避免匿名类型跨程序集访问失败,提供高性能的实体访问
|
||||
public IEnumerable<FrameRequirement> GetRequirements()
|
||||
{
|
||||
return _requirements.Values;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user