C# 海康威视摄像头二次开发入门:HCNetSDK 登录、实时预览、云台控制、录像与抓图(WinForms 实战)

C# 海康威视摄像头二次开发入门:HCNetSDK 登录、实时预览、云台控制、录像与抓图(WinForms 实战)

想在自己的程序里接入海康摄像头(实时画面、控制云台转向、录像、拍照),就绕不开海康官方的 HCNetSDK 。它本质是一套 C/C++ 编写的非托管 DLL,C# 通过 P/Invoke(DllImport)来调用。本文用一个完整的 WinForms 练习程序,带你走完海康摄像头二开的标准流程:SDK 初始化 → 设备登录 → 实时预览 → 云台 PTZ 控制 → 录像 → JPEG 抓图,每一步都给出代码、关键参数和错误排查。适合第一次接触海康 SDK、有 C# 基础的初学者。


一、海康 SDK 二次开发准备

1.1 SDK 是什么、从哪获取

海康为各类网络摄像机、NVR、球机提供了统一的 设备网络 SDK(HCNetSDK)。我们常用的功能(登录、预览、回放、云台、布防、抓图、参数配置)都封装在里面。SDK 可以在海康威视开放平台下载,注意选择与自己程序匹配的版本(本文使用 64 位库)。

解压后的 SDK 里,开发主要用到两部分:

  • 库文件(根目录的 dll) :如 HCNetSDK.dll、HCCore.dll、PlayCtrl.dll(播放库)、hlog.dll、hpr.dll、MP_Render.dll、SuperRender.dll 以及 libcrypto/libssl 等依赖库;
  • HCNetSDKCom 文件夹 :按功能拆分的组件库,如 HCPreview.dll(预览)、HCPlayBack.dll(回放)、HCAlarm.dll(报警布防)、HCCoreDevCfg.dll(配置)等。

海康 SDK 还提供了各语言的调用示例,其中 C# 示例里的 CHCNetSDK.cs 已经用 DllImport 把成百上千个函数、结构体、常量全部声明好了。我们直接把这个文件引入工程即可,不需要自己从头声明。

1.2 工程搭建(初学者最容易踩的环境坑)

  1. 新建 WinForms 项目(本文为 .NET Framework 4.7.2),把官方示例中的 CHCNetSDK.cs 加入项目;
  2. 平台必须和 DLL 一致 :64 位 DLL 就把项目目标平台设为 x64(项目属性 → 生成 → 目标平台),否则会报"无法加载 DLL";
  3. 把 SDK 的库文件 dll 和整个 HCNetSDKCom 文件夹拷贝到程序运行目录(bin\Debug);
  4. C# 调用非托管 DLL 的原理是 P/Invoke,形如:
csharp 复制代码
// CHCNetSDK.cs 内部就是这样声明 SDK 函数的,DllImport 指定非托管 dll
[DllImport("HCNetSDK.dll")]
public static extern bool NET_DVR_Init();

只要理解"CHCNetSDK 这个静态类里封装了所有 SDK 函数,我们像调用普通 C# 静态方法一样调用它们"即可。


二、程序界面与运行效果

界面分为几个区域:

  • 左侧一个大的 PictureBox:用来显示摄像头实时画面;
  • 登录授权、画面预览 按钮;
  • 云台控制区 :上、下、左、右方向键,一个 Auto 自动巡航按钮,以及一个云台速度下拉框(1~7 档);
  • Start Record / Stop Record:开始/停止录像;
  • 拍照:抓取一张 JPEG。

【插图:程序运行效果图】


三、整体调用流程与两个关键"句柄"

海康 SDK 的调用有严格顺序,后面的功能依赖前面拿到的句柄:

两个必须理解的概念:

  • 登录句柄 m_userId:登录成功后返回,代表"和这台设备建立的会话",抓图等设备级操作用它;
  • 预览句柄 m_lRealHandle:开始预览后返回,代表"这一路实时画面",云台、录像等针对画面的操作用它。

句柄返回 -1(或 0、false)表示失败 ,此时立刻调用 NET_DVR_GetLastError() 拿错误码排查。

窗体中先声明这两个句柄并初始化:

csharp 复制代码
private int m_userId = -1;      // 设备登录句柄
public int m_lRealHandle = -1;  // 预览播放句柄

四、第一步:SDK 初始化

在窗体加载事件里初始化 SDK,并设置连接超时时间:

csharp 复制代码
private void Form1_Load(object sender, EventArgs e)
{
    // 初始化 SDK,整个程序只需要调用一次;返回 false 表示失败
    bool initSuccess = CHCNetSDK.NET_DVR_Init();
    if (!initSuccess)
    {
        MessageBox.Show($"sdk初始化失败,错误码:{CHCNetSDK.NET_DVR_GetLastError()}");
    }
    else
    {
        // 设置连接设备的等待时间(毫秒)与重试次数,还可按需配置断线重连等
        CHCNetSDK.NET_DVR_SetConnectTime(2000, 1);
    }
}

五、第二步:设备登录

通过 IP、端口(海康默认 8000 )、用户名、密码登录设备。登录成功会返回 m_userId,设备信息(含序列号)写入 NET_DVR_DEVICEINFO_V30 结构体:

csharp 复制代码
private void button1_Click(object sender, EventArgs e)
{
    // 准备设备信息结构体,登录成功后会被填充
    CHCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V30();

    // 登录:IP、端口、用户名、密码、设备信息(引用传递)
    // 返回 -1 失败,其它值即用户 ID(登录句柄)
    m_userId = CHCNetSDK.NET_DVR_Login_V30("192.168.215.64", 8000,
                                           "admin", "你的密码", ref deviceInfo);
    if (m_userId < 0)
    {
        int errCode = (int)CHCNetSDK.NET_DVR_GetLastError();
        MessageBox.Show($"相机登录失败,错误码: {errCode}");
    }
    else
    {
        // sSerialNumber 是字节数组,转成字符串得到设备序列号
        MessageBox.Show($"相机登录成功,设备序列号: " +
                        Encoding.ASCII.GetString(deviceInfo.sSerialNumber));
    }
}

练习时把 IP、账号、密码直接写在代码里没问题;正式项目建议放到配置文件,并保证电脑和摄像头在同一网段、能 ping 通。


六、第三步:实时预览

登录成功后,填充 NET_DVR_PREVIEWINFO 结构体指定播放窗口、通道、码流、连接方式,然后调用 NET_DVR_RealPlay_V40 开始预览:

csharp 复制代码
private void button2_Click(object sender, EventArgs e)
{
    if (m_userId < 0)
    {
        MessageBox.Show("请先登录设备");
        return;
    }

    CHCNetSDK.NET_DVR_PREVIEWINFO previewInfo = new CHCNetSDK.NET_DVR_PREVIEWINFO();
    previewInfo.hPlayWnd = this.pictureBox1.Handle; // 画面渲染到哪个控件:给 PictureBox 的句柄
    previewInfo.lChannel = 1;     // 通道号,网络摄像机默认 1 通道
    previewInfo.dwStreamType = 0; // 码流类型:0 主码流,1 子码流
    previewInfo.dwLinkMode = 0;   // 连接模式:0 TCP,1 UDP,...

    // 开始实时预览:传入登录句柄、预览参数;回调传 null(直接渲染到窗口)
    // 返回预览句柄,<0 表示失败
    m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(m_userId, ref previewInfo,
                                                   null, IntPtr.Zero);
    if (m_lRealHandle < 0)
    {
        int errCode = (int)CHCNetSDK.NET_DVR_GetLastError();
        MessageBox.Show($"实时预览失败,错误码:{errCode}");
    }
}

如果不想让 SDK 直接渲染画面,而是想自己拿到每一帧码流(例如做转码、AI 分析),可以把第三个参数传一个码流回调函数,再配合播放库 PlayCtrl 处理。入门阶段直接给窗口句柄最简单。


七、第四步:云台 PTZ 控制(方向 / 速度 / 自动巡航)

带云台的球机可以控制镜头转动。这里用的是 NET_DVR_PTZControlWithSpeed,函数签名为:

csharp 复制代码
bool NET_DVR_PTZControlWithSpeed(int lRealHandle, uint dwPTZCommand,
                                 uint dwStop, uint dwSpeed);
  • dwPTZCommand:动作命令(上/下/左/右/自动等);
  • dwStop:0 = 启动动作,1 = 停止动作;
  • dwSpeed:速度,1~7。

关键交互技巧:在方向按钮的 MouseDown(按下)事件里启动转动(dwStop=0),MouseUp(松开)事件里停止(dwStop=1),这样按住就转、松手即停。

以"向左"为例:

csharp 复制代码
// 按下"左":开始向左转,速度取下拉框选中值
private void button5_MouseDown(object sender, MouseEventArgs e)
{
    CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.PAN_LEFT,
                                          0, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
}
// 松开"左":停止转动(dwStop=1)
private void button5_MouseUp(object sender, MouseEventArgs e)
{
    CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, CHCNetSDK.PAN_LEFT,
                                          1, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
}

其余三个方向完全一样,只是命令常量不同:

csharp 复制代码
// 向上
NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.TILT_UP, 0, speed);
// 向下
NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.TILT_DOWN, 0, speed);
// 向右
NET_DVR_PTZControlWithSpeed(m_lRealHandle, (uint)CHCNetSDK.PAN_RIGHT, 0, speed);
// (对应的 MouseUp 把第三个参数改为 1 即可)

常用 PTZ 命令常量(定义在 CHCNetSDK.cs):

命令常量 值 含义
TILT_UP 21 向上
TILT_DOWN 22 向下
PAN_LEFT 23 向左
PAN_RIGHT 24 向右
PAN_AUTO 29 水平自动扫描

自动巡航用一个按钮在"启动/停止"之间切换:

csharp 复制代码
private void button5_Click(object sender, EventArgs e)
{
    if (btnAuto.Text == "Auto")
    {
        // 开启水平自动旋转,按钮文字切换为 Stop
        CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, CHCNetSDK.PAN_AUTO,
                                              0, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
        btnAuto.Text = "Stop";
    }
    else
    {
        // 再次点击:停止自动旋转
        CHCNetSDK.NET_DVR_PTZControlWithSpeed(m_lRealHandle, CHCNetSDK.PAN_AUTO,
                                              1, Convert.ToUInt32(comboBoxSpeed.SelectedItem));
        btnAuto.Text = "Auto";
    }
}

注意:云台控制必须在预览成功之后(需要预览句柄),且摄像头本身要支持云台(固定枪机调用会报错)。


八、第五步:录像(保存实时码流)

录像用 NET_DVR_SaveRealData 开始把实时码流写入文件,用 NET_DVR_StopSaveRealData 停止。同样要先开预览:

csharp 复制代码
private string m_recordDir = Directory.GetCurrentDirectory() + "\\录像文件\\";

private void btnRecord_Click(object sender, EventArgs e)
{
    if (m_lRealHandle < 0)
    {
        MessageBox.Show("请先开启画面预览!");
        return;
    }
    // 目录不存在则自动创建
    if (!Directory.Exists(m_recordDir))
        Directory.CreateDirectory(m_recordDir);

    // 用时间戳生成文件名,避免覆盖
    string fileName = m_recordDir + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".mp4";

    // 开始保存实时数据(录像)
    bool result = CHCNetSDK.NET_DVR_SaveRealData(m_lRealHandle, fileName);
    if (!result)
        MessageBox.Show($"开始录像失败,错误码:{CHCNetSDK.NET_DVR_GetLastError()}");
    else
        MessageBox.Show($"录像已开始\n保存路径:{fileName}");
}

// 停止录像
private void StopRecord_Click(object sender, EventArgs e)
{
    if (m_lRealHandle < 0) return;
    bool result = CHCNetSDK.NET_DVR_StopSaveRealData(m_lRealHandle);
    if (!result)
        MessageBox.Show($"结束录像失败,错误码:{CHCNetSDK.NET_DVR_GetLastError()}");
    else
        MessageBox.Show("录像已结束,文件已保存");
}

九、第六步:JPEG 抓图(拍照)

抓图用 NET_DVR_CaptureJPEGPicture,它使用登录句柄 和通道号,并通过 NET_DVR_JPEGPARA 指定图片质量与分辨率:

csharp 复制代码
private string m_rJPEGDir = Directory.GetCurrentDirectory() + "\\图片文件\\";

private void btnJPEG_Click(object sender, EventArgs e)
{
    if (!Directory.Exists(m_rJPEGDir))
        Directory.CreateDirectory(m_rJPEGDir);

    // 用时间戳命名 jpg
    string sJpegPicFileName = m_rJPEGDir + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".jpg";

    int lChannel = 1; // 通道号

    CHCNetSDK.NET_DVR_JPEGPARA lpJpegPara = new CHCNetSDK.NET_DVR_JPEGPARA();
    lpJpegPara.wPicQuality = 0;     // 图像质量
    lpJpegPara.wPicSize = 0xff;     // 抓图分辨率:0xff = Auto,使用当前码流分辨率

    // JPEG 抓图:登录句柄、通道、参数、保存路径;返回 false 失败
    if (!CHCNetSDK.NET_DVR_CaptureJPEGPicture(m_userId, lChannel,
                                              ref lpJpegPara, sJpegPicFileName))
    {
        uint err = CHCNetSDK.NET_DVR_GetLastError();
        MessageBox.Show("抓图失败, error code= " + err);
    }
    else
    {
        MessageBox.Show("抓图成功,文件已保存:" + sJpegPicFileName);
    }
}

运行后即可在程序目录的 图片文件 文件夹看到拍下的 jpg 照片。

抓图还有一种方式是在预览回调里对当前帧抓图(NET_DVR_CaptureJPEGPicture_NEW 等),入门用上面的设备抓图接口即可。


十、资源释放与可改进点

本程序以"练通二开流程"为目的,功能跑通即可。工程上还可以再完善(了解即可):窗体关闭时应依次调用 NET_DVR_StopRealPlay(停止预览)、NET_DVR_Logout(登出)、NET_DVR_Cleanup(释放 SDK),并可加入断线重连、把账号参数移到配置文件等。这些属于健壮性优化,不影响理解核心调用流程。


十一、常见错误码与排查

任何 SDK 函数失败,都先用 NET_DVR_GetLastError() 取错误码,再对照官方《错误码对照表》。初学者高频问题:

现象 / 错误码 可能原因与处理
报"无法加载 DLL HCNetSDK" 平台不匹配(x86/x64)、dll 或 HCNetSDKCom 没拷到运行目录、缺依赖库
错误码 7(连接失败) 电脑和摄像头网络不通、IP 不在同一网段、端口不是 8000,先 ping 测试
错误码 1(密码错误) 用户名/密码错误;多次输错设备会临时锁定 IP,需等待或重启设备
预览黑屏 / 报错 没登录成功、通道号不对、hPlayWnd 句柄错误、设备不支持该码流
云台控制报错 没有先预览(句柄无效)、设备是不带云台的固定枪机
录像/抓图找不到文件 路径用的是相对当前目录,确认程序实际运行目录及文件夹权限

排查口诀:先通网络(ping)→ 再对平台和 DLL → 然后看登录是否成功 → 最后看错误码。


十二、小结

本文用一个 WinForms 练习程序,完整走了一遍海康 HCNetSDK 二次开发流程:

  1. 引入官方 CHCNetSDK.cs 封装,把 dll 和 HCNetSDKCom 放到运行目录、选对 x64 平台;
  2. NET_DVR_Init 初始化、NET_DVR_Login_V30 登录拿到 m_userId;
  3. NET_DVR_RealPlay_V40 把画面渲染到 PictureBox,拿到 m_lRealHandle;
  4. NET_DVR_PTZControlWithSpeed 配合 MouseDown/MouseUp 实现方向控制与速度调节,PAN_AUTO 实现自动巡航;
  5. NET_DVR_SaveRealData / StopSaveRealData 录像,NET_DVR_CaptureJPEGPicture 抓 JPEG;
  6. 任何失败都用 NET_DVR_GetLastError 取错误码定位。

掌握这套"初始化---登录---预览---业务操作---释放"的固定套路后,再去做回放、报警布防、参数配置等更高级功能就会轻松很多。建议对照本文代码,接一台真实摄像头(或用海康模拟器)亲手跑一遍。


相关推荐
数据狐(Datafox)1 小时前
淘宝商品详情API实战:多语言代购商城自动同步数据完整方案
开发语言·前端·数据库·爬虫·json
芯次元玩家1 小时前
技术岗转物联网解决方案架构师,行业洞察、商业模式该从哪开始学?
java·开发语言
2601_962218611 小时前
C++ string 类原理、踩坑与对象语义详解
开发语言·c++
隔窗听雨眠1 小时前
FunProxy用Rust构建跨平台全链路测试抓包代理工具
开发语言·后端·rust
y = xⁿ1 小时前
关于Agent智能体开发
开发语言·前端·javascript
Zhou1411361 小时前
MyBatisPlus_02_条件构造器与高级功能
java·开发语言·python
geovindu1 小时前
rust: search
开发语言·后端·rust
爱上网的小舟2 小时前
windows无法启动windows audio服务怎么办?6个步骤排查修复windows audio无法启动
windows
淡海水2 小时前
16-03-C#数据结构源码-附录C-参考资源
unity·c#·游戏引擎·il2cpp