Files
Ayay/SHH.CameraSdk/Drivers/HikVision/HikSdkManager.cs

125 lines
5.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace SHH.CameraSdk;
/// <summary>
/// [驱动支持层] 海康 SDK 全局资源管理器 (V3.3.1 修复版)
/// <para>核心修复:</para>
/// <para>1. [Bug S] 引用计数保护:增加下溢检测,防止异常销毁流程导致的计数器错乱。</para>
/// <para>2. [Bug C] 禁用内部重连:确保 SDK 不会背着上层偷偷重连,彻底消除僵尸连接。</para>
/// </summary>
public static class HikSdkManager
{
#region --- (Global States & Locks) ---
/// <summary>
/// 全局引用计数器。
/// 只有当计数从 0 变 1 时才进行物理初始化,从 1 变 0 时才物理卸载。
/// </summary>
private static int _referenceCount = 0;
/// <summary>
/// 静态同步锁。
/// 用于保护 _referenceCount 的原子操作,防止多线程并发 Start/Stop 时导致的初始化冲突。
/// </summary>
private static readonly object _lock = new();
/// <summary>
/// 播放库预热状态标记。
/// 用于避免重复执行硬件探测(首次预热后后续直接返回)。
/// </summary>
private static bool _isWarmedUp = false;
#endregion
#region --- SDK (SDK Initialization & Uninstallation) ---
/// <summary>
/// 全局初始化海康 SDK 环境。
/// <para>此方法是幂等的,内部会自动增加引用计数,支持多线程并发调用。</para>
/// </summary>
/// <returns>初始化成功返回 true若 SDK 核心组件HCNetSDK.dll加载失败则返回 false。</returns>
public static bool Initialize()
{
lock (_lock)
{
// 引用计数为 0 时执行物理初始化(仅首次调用时触发)
if (_referenceCount == 0)
{
// [物理初始化] 调用海康核心 DLL 接口,初始化 SDK 基础环境
if (!HikNativeMethods.NET_DVR_Init()) return false;
// --- 工业级可靠性设置(注释保留,按需启用)---
//// 1. 登录超时设置 (3000ms)
//// 在高并发场景下,快速失败比无限重试更有利于系统调度。
//HikNativeMethods.NET_DVR_SetConnectTime(3000, 1);
//// 2. [Fix Bug C: 双重重连冲突]
//// 设计思路:禁用海康 SDK 内部的自动重连机制bEnableRecon = false
//// 理由SDK 内部重连是非透明的,无法与我们的上层协调器 (Coordinator) 状态机完美对齐。
//// 统一由外层协调器负责“检测断线 -> 销毁旧句柄 -> 重新登录”,确保状态的一致性。
//HikNativeMethods.NET_DVR_SetReconnect(10000, false);
}
// 引用计数递增,记录当前活跃的 SDK 使用者数量
_referenceCount++;
return true;
}
}
/// <summary>
/// 全局卸载海康 SDK 环境。
/// <para>当所有相机实例都停止并释放后(引用计数归 0会真正释放非托管资源。</para>
/// </summary>
public static void Uninitialize()
{
lock (_lock)
{
// [Fix Bug S: 引用计数溢出保护]
// 确保不会因为意外的多次调用导致计数器变为负数,避免逻辑异常
if (_referenceCount > 0)
{
_referenceCount--;
// 引用计数归 0 时执行物理卸载,关闭 SDK 所有隐形线程与资源
if (_referenceCount == 0)
{
// [物理卸载] 释放 SDK 占用的非托管资源(如网络连接、内存缓冲区)
HikNativeMethods.NET_DVR_Cleanup();
}
}
}
}
#endregion
#region --- (PlayCtrl Warm-up) ---
/// <summary>
/// [核心策略] 强制冷启动诱发
/// 职责:在系统真正取流前,强行触发一次 PlayCtrl.dll 的硬件探测声卡、显卡、DirectDraw
/// 目的:规避首次取流时的 12-18 秒延迟,提前完成硬件初始化
/// </summary>
public static void ForceWarmUp()
{
// 已预热过则直接返回,避免重复执行
if (_isWarmedUp) return;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 正在进行播放库硬件探测预热,请稍候...");
Stopwatch sw = Stopwatch.StartNew();
int tempPort = -1;
// 第一次调用 PlayM4_GetPort触发 PlayCtrl.dll 底层硬件初始化(耗时主要集中在这里)
if (HikPlayMethods.PlayM4_GetPort(ref tempPort))
{
// 必须释放临时端口避免端口资源泄漏PlayCtrl.dll 端口数量有限)
HikPlayMethods.PlayM4_FreePort(tempPort);
}
sw.Stop();
_isWarmedUp = true;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 预热完成!耗时: {sw.ElapsedMilliseconds}ms。后续调用将恢复正常。");
}
#endregion
}