增加 SmartFrame 强制收回逻辑
This commit is contained in:
@@ -85,8 +85,8 @@ public static class HikSdkManager
|
||||
// 引用计数归 0 时执行物理卸载,关闭 SDK 所有隐形线程与资源
|
||||
if (_referenceCount == 0)
|
||||
{
|
||||
// [物理卸载] 释放 SDK 占用的非托管资源(如网络连接、内存缓冲区)
|
||||
HikNativeMethods.NET_DVR_Cleanup();
|
||||
//// [物理卸载] 释放 SDK 占用的非托管资源(如网络连接、内存缓冲区)
|
||||
//HikNativeMethods.NET_DVR_Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 > 0,MarkFrameReceived 内部只会累加码流,不会增加 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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user