修复 Bug

This commit is contained in:
2026-01-17 13:13:17 +08:00
parent a27045e0a0
commit 8482996a94
10 changed files with 177 additions and 59 deletions

View File

@@ -537,7 +537,7 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
}
catch (Exception ex)
{
Debug.WriteLine($"[UIEventError] 设备 {Id} 状态回调异常: {ex.Message}");
_sdkLog.Error(ex, "设备 {Id} 状态变更回调异常", Id);
}
// 退出条件:取消令牌已触发 且 队列为空
@@ -554,9 +554,10 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
}
}
}
catch (OperationCanceledException) { /* 正常退出 */ }
catch (Exception ex)
{
Debug.WriteLine($"[DistributorFatal] 设备 {Id} 状态分发器崩溃: {ex.Message}");
_sdkLog.Fatal(ex, "设备 {Id} 状态分发器致命异常", Id);
}
}
@@ -670,31 +671,46 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
{
// 防止重复 Dispose
if (_isDisposed) return;
_isDisposed = true;
// 1. 停止业务逻辑
await StopAsync().ConfigureAwait(false);
// Optimized: [原因] 获取生命周期锁,防止在 DisposeAsync 执行期间被并发触发 Start/Stop 操作
await _lifecycleLock.WaitAsync().ConfigureAwait(false);
// 2. 优雅关闭状态分发器
_statusQueue.Writer.TryComplete(); // 标记队列不再接受新消息
_distributorCts?.Cancel(); // 触发分发器取消
// 3. 等待分发器处理完剩余消息(最多等待 500ms
if (_distributorTask != null)
try
{
await Task.WhenAny(_distributorTask, Task.Delay(500)).ConfigureAwait(false);
// 防止重复 Dispose
if (_isDisposed) return;
_isDisposed = true;
// 1. 停止业务逻辑
await StopAsync().ConfigureAwait(false);
// 2. 优雅关闭状态分发器
_statusQueue.Writer.TryComplete(); // 标记队列不再接受新消息
_distributorCts?.Cancel(); // 触发分发器取消
// 3. 等待分发器处理完剩余消息(最多等待 500ms
if (_distributorTask != null)
{
await Task.WhenAny(_distributorTask, Task.Delay(500)).ConfigureAwait(false);
}
// 4. 切断事件引用,防止内存泄漏
FrameReceived = null;
StatusChanged = null;
// 5. 释放基础资源
_lifecycleLock.Dispose();
_distributorCts?.Dispose();
}
finally
{
// Modified: [原因] 保证计数锁在任何情况下都能释放
if (!_isDisposed)
_lifecycleLock.Release();
// 4. 切断事件引用,防止内存泄漏
FrameReceived = null;
StatusChanged = null;
// 5. 释放基础资源
_lifecycleLock.Dispose();
_distributorCts?.Dispose();
// 6. 抑制垃圾回收器的终结器
GC.SuppressFinalize(this);
// 6. 抑制垃圾回收器的终结器
GC.SuppressFinalize(this);
}
}
#endregion

View File

@@ -419,15 +419,15 @@ public class HikVideoSource : BaseVideoSource,
#region --- (Decoding) ---
// 必须同时加上 SecurityCritical
//[HandleProcessCorruptedStateExceptions]
//[SecurityCritical]
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
private void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref HikPlayMethods.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
{
// 防御性检查,防止传入空指针导致 OpenCV 崩溃CSE异常
if (pBuf == IntPtr.Zero || nSize <= 0)
{
return;
}
//Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 帧抵达.");
// Optimized: [原因] 增加前置防御性检查,若回调入参异常立即退出,防止后续 OpenCV 封装崩溃
if (pBuf == IntPtr.Zero || nSize <= 0
|| pFrameInfo.nWidth <= 0 || pFrameInfo.nHeight <= 0) return;
// [优化] 维持心跳,防止被哨兵误杀
MarkFrameReceived(0);
@@ -463,8 +463,8 @@ public class HikVideoSource : BaseVideoSource,
bool lockTaken = false;
try
{
// 尝试获取锁,超时时间 0ms (拿不到立即返回 false)
Monitor.TryEnter(_initLock, 0, ref lockTaken);
// 尝试获取锁,超时时间 20ms (拿不到立即返回 false)
Monitor.TryEnter(_initLock, 20, ref lockTaken);
if (lockTaken)
{
@@ -478,7 +478,7 @@ public class HikVideoSource : BaseVideoSource,
}
else
{
// 【关键逻辑】没拿到锁,说明主线程正在操作 (通常是正在 Stop)
// 【关键逻辑】如果 20ms 没拿到锁,说明主线程正在操作 (通常是正在 Stop)
// 既然都要停止了,这一帧直接丢弃,立即返回,防止死锁
return;
}
@@ -491,16 +491,15 @@ public class HikVideoSource : BaseVideoSource,
if (_framePool == null) return;
// 3. 转换与分发
SmartFrame smartFrame = _framePool.Get();
// 【标志位】用于判断所有权是否成功移交
bool handoverSuccess = false;
// Optimized: [原因] 将 smartFrame 定义在 try 外部,确保 finally 块能够可靠执行 Dispose 归还逻辑
SmartFrame? smartFrame = null;
try
{
{ // 3. 转换与分发
smartFrame = _framePool.Get();
if (smartFrame == null) return; // 池满丢帧
// Optimized: [原因] 使用局部作用域封装 YUV 转换,确保原生指针尽快脱离
using (var rawYuvWrapper = Mat.FromPixelData(height + height / 2, width, MatType.CV_8UC1, pBuf))
{
Cv2.CvtColor(rawYuvWrapper, smartFrame.InternalMat, ColorConversionCodes.YUV2BGR_YV12);
@@ -519,9 +518,6 @@ public class HikVideoSource : BaseVideoSource,
// decision.TargetAppIds 包含了 "谁需要这一帧" 的信息
//GlobalProcessingCenter.Submit(this.Id, smartFrame, decision);
GlobalPipelineRouter.Enqueue(Id, smartFrame, decision);
// 标记成功,禁止 finally 块销毁对象
handoverSuccess = true;
}
catch (Exception ex)
{