2025-12-26 03:18:21 +08:00
|
|
|
|
using OpenCvSharp;
|
|
|
|
|
|
|
|
|
|
|
|
namespace SHH.CameraSdk;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-12-26 12:15:10 +08:00
|
|
|
|
/// [海康驱动] 工业级视频源实现 V3.4.0 (运维增强版)
|
|
|
|
|
|
/// 修复记录:
|
|
|
|
|
|
/// 1. [Fix Bug Z] 控制器遮蔽:移除子类 Controller 定义,复用基类实例,修复 FPS 控制失效问题。
|
|
|
|
|
|
/// 2. [Feat A] 热更新支持:实现 OnApplyOptions,支持码流/句柄不亦断线热切换。
|
|
|
|
|
|
/// 3. [Feat B] 审计集成:全面接入 AddAuditLog,对接 Web 运维仪表盘。
|
2025-12-26 03:18:21 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class HikVideoSource : BaseVideoSource
|
|
|
|
|
|
{
|
|
|
|
|
|
#region --- 静态资源 (Global Resources) ---
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 静态路由表
|
2025-12-26 03:18:21 +08:00
|
|
|
|
private static readonly ConcurrentDictionary<int, HikVideoSource> _instances = new();
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 全局异常回调
|
2025-12-26 03:18:21 +08:00
|
|
|
|
private static readonly HikNativeMethods.EXCEPTION_CALLBACK _globalExceptionCallback = StaticOnSdkException;
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 端口抢占锁
|
2025-12-26 03:18:21 +08:00
|
|
|
|
private static readonly object _globalPortLock = new();
|
2025-12-26 12:15:10 +08:00
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region --- 实例成员 (Instance Members) ---
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
private int _userId = -1; // SDK 登录句柄
|
|
|
|
|
|
private int _realPlayHandle = -1; // 预览句柄
|
|
|
|
|
|
private int _playPort = -1; // 播放端口
|
|
|
|
|
|
|
|
|
|
|
|
private readonly object _initLock = new();
|
|
|
|
|
|
private readonly object _bufferLock = new();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
private volatile int _connectionEpoch = 0;
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 回调委托引用 (防止GC)
|
2025-12-26 03:18:21 +08:00
|
|
|
|
private HikNativeMethods.REALDATACALLBACK? _realDataCallBack;
|
|
|
|
|
|
private HikPlayMethods.DECCBFUN? _decCallBack;
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 内存复用对象
|
2025-12-26 03:18:21 +08:00
|
|
|
|
private Mat? _sharedYuvMat;
|
2025-12-26 12:15:10 +08:00
|
|
|
|
private Mat? _sharedBgrMat; // (如有需要可复用,当前逻辑直接用FramePool)
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
private FramePool? _framePool;
|
2025-12-26 12:15:10 +08:00
|
|
|
|
private bool _isPoolReady = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 【关键修复 Bug Z】: 删除了这里原本的 "public FrameController Controller..."
|
|
|
|
|
|
// 直接使用 BaseVideoSource.Controller
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region --- 构造函数 (Constructor) ---
|
2025-12-26 12:15:10 +08:00
|
|
|
|
|
|
|
|
|
|
public HikVideoSource(VideoSourceConfig config) : base(config)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 构造函数保持简洁
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region --- 核心生命周期 (Core Lifecycle) ---
|
2025-12-26 12:15:10 +08:00
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
protected override async Task OnStartAsync(CancellationToken token)
|
|
|
|
|
|
{
|
|
|
|
|
|
int currentEpoch = Interlocked.Increment(ref _connectionEpoch);
|
|
|
|
|
|
|
|
|
|
|
|
await Task.Run(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (currentEpoch != _connectionEpoch) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (!HikSdkManager.Initialize())
|
|
|
|
|
|
throw new CameraException(CameraErrorCode.SdkNotInitialized, "SDK初始化失败", DeviceBrand.HikVision);
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
HikNativeMethods.NET_DVR_SetExceptionCallBack_V30(0, IntPtr.Zero, _globalExceptionCallback, IntPtr.Zero);
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// [审计] 记录登录动作
|
|
|
|
|
|
AddAuditLog($"正在执行物理登录... ({_config.IpAddress})");
|
|
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
var devInfo = new HikNativeMethods.NET_DEVICEINFO_V30();
|
|
|
|
|
|
int newUserId = HikNativeMethods.NET_DVR_Login_V30(
|
|
|
|
|
|
_config.IpAddress, _config.Port, _config.Username, _config.Password, ref devInfo);
|
|
|
|
|
|
|
|
|
|
|
|
if (currentEpoch != _connectionEpoch)
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
if (newUserId >= 0) HikNativeMethods.NET_DVR_Logout(newUserId);
|
|
|
|
|
|
throw new OperationCanceledException("启动任务已过期");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_userId = newUserId;
|
|
|
|
|
|
if (_userId < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint err = HikNativeMethods.NET_DVR_GetLastError();
|
|
|
|
|
|
throw new CameraException(HikErrorMapper.Map(err), $"登录失败: {err}", DeviceBrand.HikVision, (int)err);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_instances.TryAdd(_userId, this);
|
2025-12-26 12:15:10 +08:00
|
|
|
|
AddAuditLog($"物理登录成功 (UserID: {_userId})");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 开启取流
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (!StartRealPlay())
|
|
|
|
|
|
{
|
|
|
|
|
|
uint err = HikNativeMethods.NET_DVR_GetLastError();
|
|
|
|
|
|
throw new CameraException(HikErrorMapper.Map(err), $"预览失败: {err}", DeviceBrand.HikVision, (int)err);
|
|
|
|
|
|
}
|
2025-12-26 12:15:10 +08:00
|
|
|
|
|
|
|
|
|
|
AddAuditLog($"网络取流成功 (StreamType: {_config.StreamType})");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
2025-12-26 12:15:10 +08:00
|
|
|
|
catch (Exception ex)
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
AddAuditLog($"启动异常: {ex.Message}");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
CleanupSync();
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}, token);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override async Task OnStopAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
Interlocked.Increment(ref _connectionEpoch);
|
2025-12-26 12:15:10 +08:00
|
|
|
|
AddAuditLog("正在执行停止流程...");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
await Task.Run(() => CleanupSync());
|
2025-12-26 12:15:10 +08:00
|
|
|
|
AddAuditLog("设备已停止");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CleanupSync()
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_initLock)
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 1. 停止预览
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (_realPlayHandle >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
HikNativeMethods.NET_DVR_StopRealPlay(_realPlayHandle);
|
|
|
|
|
|
_realPlayHandle = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 2. 停止解码
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (_playPort >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
HikPlayMethods.PlayM4_Stop(_playPort);
|
|
|
|
|
|
HikPlayMethods.PlayM4_CloseStream(_playPort);
|
|
|
|
|
|
HikPlayMethods.PlayM4_FreePort(_playPort);
|
|
|
|
|
|
_playPort = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lock (_bufferLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
_sharedYuvMat?.Dispose(); _sharedYuvMat = null;
|
|
|
|
|
|
_sharedBgrMat?.Dispose(); _sharedBgrMat = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 3. 注销登录
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (_userId >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
_instances.TryRemove(_userId, out _);
|
|
|
|
|
|
HikNativeMethods.NET_DVR_Logout(_userId);
|
|
|
|
|
|
_userId = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_framePool?.Dispose();
|
|
|
|
|
|
_framePool = null;
|
|
|
|
|
|
_isPoolReady = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
HikSdkManager.Uninitialize();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
#region --- [新功能] 动态参数热应用 (Hot Swap) ---
|
|
|
|
|
|
|
|
|
|
|
|
// 【关键修复 Feat A】实现基类的抽象方法,处理码流切换
|
|
|
|
|
|
protected override void OnApplyOptions(DynamicStreamOptions options)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. 码流热切换逻辑
|
|
|
|
|
|
if (options.StreamType.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
int targetStream = options.StreamType.Value;
|
|
|
|
|
|
AddAuditLog($"收到码流切换请求: {targetStream},开始执行热切换...");
|
|
|
|
|
|
|
|
|
|
|
|
lock (_initLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
// A. 停止预览 (Keep Login)
|
|
|
|
|
|
if (_realPlayHandle >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
HikNativeMethods.NET_DVR_StopRealPlay(_realPlayHandle);
|
|
|
|
|
|
_realPlayHandle = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// B. 清理播放库 (防止旧流数据残留)
|
|
|
|
|
|
if (_playPort >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
HikPlayMethods.PlayM4_Stop(_playPort);
|
|
|
|
|
|
HikPlayMethods.PlayM4_CloseStream(_playPort);
|
|
|
|
|
|
HikPlayMethods.PlayM4_FreePort(_playPort);
|
|
|
|
|
|
_playPort = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// C. 更新内部配置状态
|
|
|
|
|
|
_config.StreamType = targetStream;
|
|
|
|
|
|
|
|
|
|
|
|
// D. 重新开启预览
|
|
|
|
|
|
if (StartRealPlay())
|
|
|
|
|
|
{
|
|
|
|
|
|
AddAuditLog($"码流热切换成功 (当前: {(_config.StreamType == 0 ? "主" : "子")}码流)");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
uint err = HikNativeMethods.NET_DVR_GetLastError();
|
|
|
|
|
|
AddAuditLog($"码流切换失败: {err}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 句柄动态更新逻辑 (如有需要)
|
|
|
|
|
|
if (options.RenderHandle.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果是硬解码模式,可以在这里调用 PlayM4_Play(port, newHandle)
|
|
|
|
|
|
AddAuditLog($"收到新句柄绑定请求: {options.RenderHandle}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
#region --- 网络取流 (Network Streaming) ---
|
|
|
|
|
|
|
|
|
|
|
|
private bool StartRealPlay()
|
|
|
|
|
|
{
|
|
|
|
|
|
var previewInfo = new HikNativeMethods.NET_DVR_PREVIEWINFO
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
hPlayWnd = IntPtr.Zero,
|
|
|
|
|
|
lChannel = _config.ChannelIndex,
|
|
|
|
|
|
dwStreamType = (uint)_config.StreamType,
|
|
|
|
|
|
bBlocked = false
|
2025-12-26 03:18:21 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
_realDataCallBack = new HikNativeMethods.REALDATACALLBACK(SafeOnRealDataReceived);
|
|
|
|
|
|
_realPlayHandle = HikNativeMethods.NET_DVR_RealPlay_V40(_userId, ref previewInfo, _realDataCallBack, IntPtr.Zero);
|
|
|
|
|
|
|
|
|
|
|
|
return _realPlayHandle >= 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void SafeOnRealDataReceived(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr pUser)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// [优化] 维持心跳,防止被哨兵误杀
|
|
|
|
|
|
MarkFrameReceived(dwBufSize);
|
|
|
|
|
|
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (_realPlayHandle == -1) return;
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 处理系统头
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (dwDataType == HikNativeMethods.NET_DVR_SYSHEAD && _playPort == -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_initLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_realPlayHandle == -1 || _playPort != -1) return;
|
|
|
|
|
|
|
|
|
|
|
|
bool getPortSuccess;
|
|
|
|
|
|
lock (_globalPortLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
getPortSuccess = HikPlayMethods.PlayM4_GetPort(ref _playPort);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!getPortSuccess) return;
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
HikPlayMethods.PlayM4_SetDisplayBuf(_playPort, 1); // 极速模式
|
|
|
|
|
|
HikPlayMethods.PlayM4_SetStreamOpenMode(_playPort, 0);
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
if (!HikPlayMethods.PlayM4_OpenStream(_playPort, pBuffer, dwBufSize, 2 * 1024 * 1024))
|
|
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 处理流数据
|
2025-12-26 03:18:21 +08:00
|
|
|
|
else if (dwDataType == HikNativeMethods.NET_DVR_STREAMDATA && _playPort != -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
HikPlayMethods.PlayM4_InputData(_playPort, pBuffer, dwBufSize);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-26 12:15:10 +08:00
|
|
|
|
catch { }
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
#region --- 解码与帧分发 (Decoding) ---
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
private void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref HikPlayMethods.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 1. [核心流控] 询问基类控制器:这帧要不要?
|
|
|
|
|
|
// 之前失效是因为操作的是子类被遮蔽的 Controller,现在复用基类 Controller,逻辑就通了。
|
2025-12-26 03:18:21 +08:00
|
|
|
|
var decision = Controller.MakeDecision(Environment.TickCount64);
|
2025-12-26 12:15:10 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果没人要,直接丢弃,不进行 Mat 转换,节省 CPU
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (!decision.IsCaptured) return;
|
|
|
|
|
|
|
|
|
|
|
|
int width = pFrameInfo.nWidth;
|
|
|
|
|
|
int height = pFrameInfo.nHeight;
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 2. 初始化帧池
|
2025-12-26 03:18:21 +08:00
|
|
|
|
if (!_isPoolReady)
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_initLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_isPoolReady)
|
|
|
|
|
|
{
|
|
|
|
|
|
_framePool?.Dispose();
|
|
|
|
|
|
_framePool = new FramePool(width, height, MatType.CV_8UC3, initialSize: 3, maxSize: 5);
|
|
|
|
|
|
_isPoolReady = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_framePool == null) return;
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 3. 转换与分发
|
2025-12-26 03:18:21 +08:00
|
|
|
|
SmartFrame smartFrame = _framePool.Get();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
if (smartFrame == null) return; // 池满丢帧
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
using (var rawYuvWrapper = Mat.FromPixelData(height + height / 2, width, MatType.CV_8UC1, pBuf))
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
Cv2.CvtColor(rawYuvWrapper, smartFrame.InternalMat, ColorConversionCodes.YUV2BGR_YV12);
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
// 4. [分发] 将决策结果传递给处理中心
|
|
|
|
|
|
// decision.TargetAppIds 包含了 "谁需要这一帧" 的信息
|
2025-12-26 03:18:21 +08:00
|
|
|
|
GlobalProcessingCenter.Submit(this.Id, smartFrame, decision);
|
|
|
|
|
|
}
|
2025-12-26 12:15:10 +08:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
smartFrame.Dispose();
|
|
|
|
|
|
// 这里为了性能不频繁写日志,仅在调试时开启
|
|
|
|
|
|
// Debug.WriteLine(ex.Message);
|
|
|
|
|
|
}
|
2025-12-26 03:18:21 +08:00
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
smartFrame.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
#region --- 异常处理 ---
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
|
|
|
|
|
private static void StaticOnSdkException(uint dwType, int lUserID, int lHandle, IntPtr pUser)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_instances.TryGetValue(lUserID, out var instance))
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
instance.AddAuditLog($"SDK报警异常: 0x{dwType:X}"); // 写入审计
|
2025-12-26 03:18:21 +08:00
|
|
|
|
instance.ReportError(new CameraException(
|
|
|
|
|
|
CameraErrorCode.NetworkUnreachable,
|
2025-12-26 12:15:10 +08:00
|
|
|
|
$"SDK全局异常: 0x{dwType:X}",
|
2025-12-26 03:18:21 +08:00
|
|
|
|
DeviceBrand.HikVision));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-26 12:15:10 +08:00
|
|
|
|
catch { }
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
protected override Task<DeviceMetadata> OnFetchMetadataAsync() => Task.FromResult(new DeviceMetadata());
|
|
|
|
|
|
}
|