2026-01-15 18:56:39 +08:00
|
|
|
|
using Ayay.SerilogLogs;
|
|
|
|
|
|
using Serilog;
|
|
|
|
|
|
|
|
|
|
|
|
namespace SHH.CameraSdk;
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
/// <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)
|
|
|
|
|
|
{
|
2026-01-17 15:41:55 +08:00
|
|
|
|
//// [物理卸载] 释放 SDK 占用的非托管资源(如网络连接、内存缓冲区)
|
|
|
|
|
|
//HikNativeMethods.NET_DVR_Cleanup();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region --- 播放库预热 (PlayCtrl Warm-up) ---
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// [核心策略] 强制冷启动诱发
|
|
|
|
|
|
/// 职责:在系统真正取流前,强行触发一次 PlayCtrl.dll 的硬件探测(声卡、显卡、DirectDraw)
|
|
|
|
|
|
/// 目的:规避首次取流时的 12-18 秒延迟,提前完成硬件初始化
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static void ForceWarmUp()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 已预热过则直接返回,避免重复执行
|
|
|
|
|
|
if (_isWarmedUp) return;
|
|
|
|
|
|
|
2026-01-15 18:56:39 +08:00
|
|
|
|
Log.ForContext("SourceContext", LogModules.Core)
|
|
|
|
|
|
.Debug($"正在进行播放库硬件探测预热,请稍候...");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2026-01-15 18:56:39 +08:00
|
|
|
|
Log.ForContext("SourceContext", LogModules.Core)
|
|
|
|
|
|
.Debug($"预热完成!耗时: {sw.ElapsedMilliseconds}ms.");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|