增加 SmartFrame 强制收回逻辑

This commit is contained in:
2026-01-17 15:41:55 +08:00
parent e06c60968d
commit 2992306056
7 changed files with 205 additions and 48 deletions

View File

@@ -259,30 +259,33 @@ public class HikVideoSource : BaseVideoSource,
// 2. 停止解码
if (_playPort >= 0)
{
try
{
HikPlayMethods.PlayM4_Stop(_playPort);
HikPlayMethods.PlayM4_CloseStream(_playPort);
}
catch (Exception ex)
{
_sdkLog.Debug($"[SDK] Hik 停止解码失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
AddAuditLog($"[SDK] Hik 停止解码失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
}
finally
lock(_globalPortLock)
{
try
{
HikPlayMethods.PlayM4_FreePort(_playPort);
HikPlayMethods.PlayM4_Stop(_playPort);
HikPlayMethods.PlayM4_CloseStream(_playPort);
}
catch (Exception ex)
{
_sdkLog.Warning($"[SDK] Hik 端口资源释放失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
AddAuditLog($"[SDK] Hik 端口资源释放失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
_sdkLog.Debug($"[SDK] Hik 停止解码失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
AddAuditLog($"[SDK] Hik 停止解码失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
}
finally
{
try
{
HikPlayMethods.PlayM4_FreePort(_playPort);
}
catch (Exception ex)
{
_sdkLog.Warning($"[SDK] Hik 端口资源释放失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}" + "Exception: {Exp}", ex);
AddAuditLog($"[SDK] Hik 端口资源释放失败. => ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId} Exception: {ex.Message}");
}
}
_playPort = -1;
}
_playPort = -1;
}
lock (_bufferLock)
@@ -443,13 +446,15 @@ public class HikVideoSource : BaseVideoSource,
// 因为 dwBufSize > 0MarkFrameReceived 内部只会累加码流,不会增加 FPS 计数
MarkFrameReceived(dwBufSize);
// Optimized: [原因] 增加前置失效判定,若当前句柄已释放则不再处理后续流数据
if (_realPlayHandle == -1) return;
// 处理系统头
if (dwDataType == HikNativeMethods.NET_DVR_SYSHEAD && _playPort == -1)
if (dwDataType == HikNativeMethods.NET_DVR_SYSHEAD)
{
lock (_initLock)
{
// 原子检查:若已存在端口、预览句柄已失效或对象已销毁,则立即拦截
if (_realPlayHandle == -1 || _playPort != -1) return;
bool getPortSuccess;
@@ -460,19 +465,26 @@ public class HikVideoSource : BaseVideoSource,
if (!getPortSuccess) return;
// 配置播放库参数
HikPlayMethods.PlayM4_SetDisplayBuf(_playPort, 1); // 极速模式
HikPlayMethods.PlayM4_SetStreamOpenMode(_playPort, 0);
if (!HikPlayMethods.PlayM4_OpenStream(_playPort, pBuffer, dwBufSize, 2 * 1024 * 1024))
{
HikPlayMethods.PlayM4_FreePort(_playPort);
_playPort = -1;
// 开启失败需在锁内立即释放端口,防止句柄残留
lock (_globalPortLock)
{
HikPlayMethods.PlayM4_FreePort(_playPort);
_playPort = -1;
}
return;
}
_decCallBack = new HikPlayMethods.DECCBFUN(SafeOnDecodingCallBack);
HikPlayMethods.PlayM4_SetDecCallBackEx(_playPort, _decCallBack, IntPtr.Zero, 0);
HikPlayMethods.PlayM4_Play(_playPort, IntPtr.Zero);
_sdkLog.Debug($"[SDK] Hik 播放端口初始化成功, ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, 播放端口:{_playPort}");
}
}
// 处理流数据
@@ -510,12 +522,56 @@ public class HikVideoSource : BaseVideoSource,
// [优化] 维持心跳,防止被哨兵误杀
MarkFrameReceived(0);
// [新增] 捕获并更新分辨率
// 只有当分辨率发生变化时才写入,减少属性赋值开销
if (Width != pFrameInfo.nWidth || Height != pFrameInfo.nHeight)
int currentWidth = pFrameInfo.nWidth;
int currentHeight = pFrameInfo.nHeight;
// 3. [核心修复点] 分辨率动态监测与帧池热重建
// Modified: [原因] 修复 Bug E当 SDK 输出的分辨率与当前帧池尺寸不符时,必须阻塞并重建资源。
// 严禁在分辨率不匹配的情况下调用 OpenCV 转换函数,防止非托管内存越界写入。
if (!_isPoolReady || Width != currentWidth || Height != currentHeight)
{
Width = pFrameInfo.nWidth;
Height = pFrameInfo.nHeight;
bool lockTaken = false;
try
{
// 尝试获取初始化锁,超时 50ms分辨率变更属于低频关键动作允许稍长等待
Monitor.TryEnter(_initLock, 50, ref lockTaken);
if (lockTaken)
{
// Double Check防止多个解码回调并发重建
if (Width != currentWidth || Height != currentHeight || !_isPoolReady)
{
_sdkLog.Warning($"[SDK] 监测到分辨率变更: {Width}x{Height} -> {currentWidth}x{currentHeight},正在重建帧池...");
// 销毁旧池(内部会释放所有 Mat 资源)
_framePool?.Dispose();
// 更新基类维护的分辨率属性
Width = currentWidth;
Height = currentHeight;
// 重建帧池initialSize 设为 3 保证高并发缓冲maxSize 设为 5 严格控制内存总额
_framePool = new FramePool(Width, Height, MatType.CV_8UC3, initialSize: 3, maxSize: 5);
_isPoolReady = true;
AddAuditLog($"分辨率热重载完成: {Width}x{Height}");
}
}
else
{
// 拿不到锁说明主线程正在 Stop 或切换配置,直接丢弃该帧防止死锁
return;
}
}
catch (Exception ex)
{
_sdkLog.Error(ex, "帧池重建失败");
return;
}
finally
{
if (lockTaken) Monitor.Exit(_initLock);
}
}
// 1. [核心流控] 询问基类控制器:这帧要不要?
@@ -526,6 +582,8 @@ public class HikVideoSource : BaseVideoSource,
// 如果没人要,直接丢弃,不进行 Mat 转换,节省 CPU
if (!decision.IsCaptured) return;
//Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 帧抵处理.");
int width = pFrameInfo.nWidth;
int height = pFrameInfo.nHeight;