共计 2335 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
在工业级视频监控项目中,海康威视设备的稳定性与 C# 生态的便捷性结合是常见需求。但实际开发中会遇到几个典型问题:

- SDK 文档晦涩:官方提供的 CHM 文档函数说明分散,回调机制描述不直观(比如预览回调的 YUV 数据格式需反复试验)
- 初始化失败 :
NET_DVR_Init()返回 false 时缺乏详细错误信息,常见于: - DLL 依赖项缺失(如缺少 HCNetSDK 依赖的 libeay32.dll)
- 未调用
NET_DVR_SetConnectTime设置超时参数 - 预览异常:黑屏问题往往源于:
- 未正确处理
REALPLAYCALLBACK的帧数据 - 网络带宽不足导致丢帧(需调整
NET_DVR_PREVIEWINFO的码流类型)
技术方案选型
对比常见技术路线:
- HCNetSDK 原生方案:
- 优势:直接访问设备所有功能(PTZ 控制、报警输入等)
- 劣势:需处理非托管内存和复杂回调
- OpenCV+RTSP:
- 优势:跨平台且代码简洁
- 劣势:无法使用设备特色功能(如智能分析结果获取)
核心流程采用海康 SDK 原生方案,关键步骤如下:
- SDK 初始化与环境检测
- 设备登录(支持 IPC/NVR 不同设备类型)
- 获取通道能力集(确定支持的视频格式)
- 启动预览并设置回调函数
- 视频流存储与转码
代码实现详解
1. SDK 安全封装
使用 SafeHandle 避免句柄泄漏:
public sealed class HikDeviceHandle : SafeHandleZeroOrMinusOneIsInvalid
{protected override bool ReleaseHandle()
{return HCNetSDK.NET_DVR_Logout(handle) &&
HCNetSDK.NET_DVR_Cleanup();}
}
2. 回调函数转委托
处理预览回调时需注意 IntPtr 到byte[]的转换:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void REALDATACALLBACK(
int lRealHandle,
uint dwDataType,
IntPtr pBuffer,
uint dwBufSize,
IntPtr pUser);
// 实际处理函数
static void OnPreviewData(int handle, uint type, IntPtr pData, uint size, IntPtr user)
{byte[] buffer = new byte[size];
Marshal.Copy(pData, buffer, 0, (int)size);
// 处理 YUV420 数据...
}
3. 带重试的登录逻辑
public HikDeviceHandle Login(string ip, ushort port, string user, string pwd, int retry = 3)
{
HCNetSDK.NET_DVR_DEVICEINFO_V30 devInfo;
for (int i = 0; i < retry; i++)
{
int userId = HCNetSDK.NET_DVR_Login_V30(ip, port, user, pwd, out devInfo);
if (userId >= 0)
return new HikDeviceHandle(userId, devInfo);
Thread.Sleep(1000);
}
throw new HikException($"登录失败,最后错误:{HCNetSDK.NET_DVR_GetLastError()}");
}
4. FFmpeg 转码关键帧处理
使用 FFmpeg.AutoGen 时需注意 GOP 对齐:
// 创建编码器上下文时指定 GOP 大小
codecContext->gop_size = 30;
// 收到 I 帧时开始新文件
if ((pPacket->flags & AV_PKT_FLAG_KEY) != 0)
{StartNewRecording();
}
避坑指南
32/64 位 DLL 加载
通过 PE 头检测避免架构不匹配:
static void LoadCorrectDll()
{
string arch = Environment.Is64BitProcess ? "x64" : "x86";
string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, arch);
SetDllDirectory(dllPath);
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string path);
网络断连处理
建议实现心跳检测机制:
// 定时检查设备在线状态
Timer checkTimer = new Timer(_ =>
{if (!HCNetSDK.NET_DVR_GetDeviceState(handle, out _))
{OnDisconnected();
}
}, null, 0, 5000);
性能优化
内存池管理
复用 YUV 缓冲区避免频繁 GC:
ConcurrentBag<byte[]> _yuvBuffers = new();
byte[] GetBuffer(int size)
{if (_yuvBuffers.TryTake(out var buf) && buf.Length >= size)
return buf;
return new byte[size];
}
多摄像头线程策略
建议采用生产者 - 消费者模式:
- 每个摄像头独立采集线程
- 共享一个写文件队列
- 使用
BlockingCollection控制并发写入
思考题
如何实现断网续传功能?可考虑:
- 本地缓存未上传的视频片段
- 使用
NET_DVR_GetLastError判断网络状态 - 设计重传队列管理待同步数据
正文完
