尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

海康威视摄像头C# Demo实战:SDK开发到预览抓图报警

海康威视摄像头C# Demo实战:SDK开发到预览抓图报警 简介海康威视摄像头C#开发示例是一套面向程序员的摄像头开发资料借助厂商SDK解决如何用C#快速接入网络摄像头的问题适用于安防监控、远程巡检、图像采集等场景。压缩包内共187个文件以92个动态链接库、11个C#源文件、8个可执行程序为主另有配置、日志、缓存等辅助文件整体约23.38MB可在Visual Studio中直接打开查看。示例涵盖设备初始化、连接管理、实时视频流获取、单帧抓拍、参数调节、事件监听与异常处理等完整流程代码按模块封装便于二次开发。开发者可对照设备、视频流、图像与事件处理模块快速上手复用参数调整与错误处理逻辑缩短接入周期。已有3903人学习下载适合初学或进阶的C#开发者可快速搭建可用的监控应用。1. 海康威视摄像头 C# Demo第一个能跑的官方 SDK 资源做上位机或者安防集成的 C# 工程师十有八九会遇到海康威视摄像头二次开发。海康官方资料不少但 C Demo 占了大头C# 程序员想直接抄作业往往要从 C 版手动翻译结构体光是 NET_DVR_Login_V40 里嵌着的几个子结构就能卡掉半天。这套海康威视摄像头 C# Demo 的价值在于它把登录、预览、抓图、报警布防这几条最常用的链路用 C# 完整实现了一遍直接打开就能对着设备调试。适合那些手里已有海康摄像头或录像机、需要在 WinForm 或 WPF 项目里快速接入实时画面和抓图的开发者。下面我按自己拆这套 Demo 的路径把它能解决什么问题、怎么跑通、哪些地方最容易翻车一次讲清楚。2. 先拆资源Demo 里到底有什么SDK 二次开发的基本套路海康这套 C# 相关的 Demo 压缩包解压后进入视野的是一整套可编译的工程文件而不是零散的几个 .cs 文件。我建议你先别急着双击 .sln花十分钟把目录结构理清楚后面能少走很多弯路。2.1 目录结构拆解Dll、头文件与源码工程的分工典型的 Demo 包解压后会呈现如下几类内容一是 HCNetSDK 相关的动态库文件包括 HCNetSDK.dll、HCCoreDevCfg.dll、HCPreview.dll、HCAlarm.dll、PlayCtrl.dll 等它们分别负责基础通信、设备配置、预览解码和播放渲染二是头文件目录里面是 HCNetSDK.h、HCNetSDKCom.h 这类 C 头文件C# 代码里很多结构体和常量定义就是从这里翻译过来的三是 AudioRender.dll 和 hc_hikvision_audio.dll 之类的音频组件预览时若要出声就靠它们。关键的是源码工程目录一般包含 .sln 解决方案、.csproj 项目文件和 Form 窗体文件。主窗体代码里会集中展示登录、预览、抓图等操作的调用流程这是整份资源里含金量最高的部分。你还会看到一些配置文件或设备参数填写界面用于指定摄像头 IP、端口、用户名和密码。看这些文件时我的建议是先打开 HCNetSDK.cs 或类似命名的封装文件这里面几乎把 C 接口逐条翻译成了 C# 的 DllImport 声明。你需要关注的是其中几个核心方法比如 NET_DVR_Init、NET_DVR_Login_V40、NET_DVR_RealPlay_V40、NET_DVR_CaptureJPEGPicture、NET_DVR_SetupAlarmChan_V41 和 NET_DVR_Logout。把这几个方法的签名、参数结构体和返回类型记下来整份 Demo 的骨架你就抓住了。2.2 SDK 操作模型从初始化到注销的四步链路海康网络 SDK 的编程模型可以浓缩成一句话初始化、登录、操作、注销。这套 C# Demo 的所有功能都建立在这条主线上。第一步是 NET_DVR_Init负责初始化 SDK 内部状态相当于打开 SDK 的大门。这一步通常会设置网络参数比如 NET_DVR_SetConnectTime 和 NET_DVR_SetReconnectTime前者控制连接超时时间后者控制断线后自动重连的间隔。我在项目里一般把连接超时设为 3000 毫秒重连间隔设为 5000 毫秒避免设备不在线时界面长时间卡住。第二步通过 NET_DVR_Login_V40 完成登录。这个接口是海康 SDK 的现代登录方式替代了老的 NET_DVR_Login_V30。登录时需要填充 NET_DVR_USER_LOGIN_INFO 结构体里面包含了设备地址、端口、用户名、密码以及一个容易忽略的 writeDeviceInetAddr 字段它用于指示是否把设备 IP 写入本地 ARP 缓存表置 0 即可。登录成功后返回一个用户句柄后续所有预览、布防、抓图操作都要带着这个句柄走。第三步是具体操作。预览用 NET_DVR_RealPlay_V40抓图用 NET_DVR_CaptureJPEGPicture报警监听用 NET_DVR_SetupAlarmChan_V41。这些操作都是异步的预览启动后图像数据通过窗口渲染或回调实时到达报警产生后通过回调消息送达。理解这一点非常重要因为很多新手以为调用完预览函数就万事大吉结果画面没出来就开始怀疑人生其实是窗口句柄或回调配置没接上。第四步是清理。操作结束后先 NET_DVR_StopRealPlay 停止预览NET_DVR_CloseAlarmChan_V30 关闭布防通道再 NET_DVR_Logout 注销登录最后 NET_DVR_Cleanup 释放 SDK 资源。这个顺序不能乱我曾见过有人直接 Cleanup 导致进程崩溃就是因为预览线程还没退出。2.3 Demo 的阅读顺序从 Program 入口到窗体逻辑拿到 Demo 源码后建议的阅读顺序是先看 Program.cs 确认入口再看主窗体的 Load 事件里有没有做 NET_DVR_Init 和用户参数加载然后跳到按钮点击事件找登录和预览的调用链。这套 Demo 通常把设备参数配置放在一个独立的窗体或面板里登录成功后主界面会显示实时视频画面。阅读时重点关注三处一处是窗体尺寸变化时预览窗口的适应逻辑也就是把预览句柄的显示区域和 pictureBox 或 panel 的尺寸绑定第二处是关闭窗体时有没有完整执行停止预览、注销、清理的收尾流程第三处是回调函数里对消息类型的判断海康报警回调通过 lCommand 参数区分不同事件比如 COMM_ALARM_RULE 是规则报警COMM_UPLOAD_PICTURE 是图片上传。// 关闭窗体时的收尾顺序不能乱 private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { if (m_lRealHandle ! -1) { NET_DVR_StopRealPlay(m_lRealHandle); // 先停预览 m_lRealHandle -1; } if (m_lUserID ! -1) { NET_DVR_Logout(m_lUserID); // 注销登录 m_lUserID -1; } NET_DVR_Cleanup(); // 最后释放 SDK }这段代码演示了标准的收尾序列。NET_DVR_StopRealPlay 需要传入预览句柄这个句柄是预览开始时系统分配的停止后置为 -1 防止重复调用。NET_DVR_Logout 传入用户句柄注册后置 -1。NET_DVR_Cleanup 不带参数它会释放整个 SDK 占用的资源。需要注意的是如果窗体被强制关闭或者进程被杀这个流程可能来不及执行所以生产环境里建议配合 try-catch-finally 保证清理逻辑一定走到。理解这份 Demo 的操作模型后你已经知道该从哪里改代码了。接下来我们把它真正跑起来看看环境和配置需要满足哪些条件。3. 环境准备与首跑从解压到看到实时画面的完整路径把 Demo 编译通过并看到画面是这份资源落地的最关键一步。这一步卡住的人最多而且问题往往不在代码本身而在环境匹配。3.1 环境配置开发工具与运行库的版本匹配Visual Studio 版本上这套 Demo 面向的是 .NET Framework 4.x 或更高版本。我用 Visual Studio 2019 打开过典型的工程没有遇到兼容性问题。打开解决方案后第一件事右键解决方案在配置管理器里看当前的活动平台。海康官方 SDK 的 C# 封装有 x86 和 x64 之分你的进程位数必须和 Dll 的位数一致这是最容易被忽略的翻车点。我一般固定使用 x64 平台因为现代 PC 和服务器基本都是 64 位系统且有的机器上 32 位进程访问大数据块容易出问题。但要注意如果你引用的第三方控件是 32 位专用那就必须整体切到 x86。另一个现实制约是部分海康播放库 PlayCtrl.dll 只有 32 位版本这意味着你需要在程序里动态判断平台选择加载路径。首次编译会遇到的典型报错是“找不到命名空间”这通常是 HCNetSDK.cs 文件没有被包含进当前项目。右键项目选择添加现有项把 HCNetSDK.cs 等源码文件加进来即可。编译通过后还有一步把 HCNetSDK.dll、PlayCtrl.dll、HCPreview.dll、hccoredevcfg.dll 等全部拷到 exe 输出目录或者直接在项目里建一个 Dll 文件夹并把它们设为复制到输出目录。如果漏拷运行时会报无法加载 DLL而且这个报错经常是英文的很不直观。3.2 设备参数与登录流程填对信息是看到画面的前提Demo 里通常有一个用户参数管理窗体用来填设备 IP、端口、用户名、密码。海康设备的默认端口是 8000默认用户名和密码根据设备批次不同有差异早期设备是 admin/12345现在新设备强制要求激活时设置密码。如果你用的是录像机不仅要填录像机的 IP还要确认录像机与电脑在同一网段且没有开启防火墙拦截 8000 端口。登录代码是这套 Demo 的核心起点// 登录海康设备 NET_DVR_Init(); // 初始化SDK NET_DVR_USER_LOGIN_INFO loginInfo new NET_DVR_USER_LOGIN_INFO(); loginInfo.sDeviceAddress ipTextBox.Text; // 设备IP如192.168.1.64 loginInfo.wPort ushort.Parse(portTextBox.Text); // 设备端口默认8000 loginInfo.sUserName userTextBox.Text; // 用户名 loginInfo.sPassword pwdTextBox.Text; // 密码 loginInfo.writeDeviceInetAddr 0; // 是否写入ARP表一般置0 NET_DVR_DEVICEINFO_V40 deviceInfo new NET_DVR_DEVICEINFO_V40(); uint userID NET_DVR_Login_V40(ref loginInfo, ref deviceInfo); if (userID 0xFFFFFFFF) // 登录失败返回0xFFFFFFFF { uint errCode NET_DVR_GetLastError(); MessageBox.Show(登录失败错误码 errCode); }这里有几个参数要注意。NET_DVR_USER_LOGIN_INFO 结构体里的 sDeviceAddress 是字符串但在 C# 封装的 DllImport 层它往往是一个字节数组因此赋值方式可能是用 Encoding 字节拷贝需要看 Demo 封装的实际情况两种写法都有。NET_DVR_DEVICEINFO_V40 是输出型结构体里面带设备通道数、设备类型等信息后续预览时如果要遍历通道就用 deviceInfo.byStartChan 和 byChanNum 这两个字段。登录失败的基本原因就三类网络不通、端口不对、用户名密码错误用 NET_DVR_GetLastError 拿到错误码对照 SDK 头文件里的错误码定义表就能定位。3.3 实时预览从回调到渲染一个句柄如何驱动画面登录成功后启动预览的代码是下一个重点。海康有两种获取视频流的方式窗口渲染和回调取流。Demo 默认用的是窗口渲染思路是传入一个控件的句柄SDK 直接在这个控件区域内绘制画面。// 启动实时预览 NET_DVR_PREVIEWINFO previewInfo new NET_DVR_PREVIEWINFO(); previewInfo.lChannel 1; // 预览通道号从1开始 previewInfo.dwStreamType 0; // 0主码流1子码流 previewInfo.dwLinkMode 0; // TCP方式1 UDP方式 previewInfo.hPlayWnd pictureBox1.Handle; // 绘制窗口句柄 previewInfo.bBlocked 1; // 阻塞式连接 int previewHandle NET_DVR_RealPlay_V40(m_lUserID, ref previewInfo, null, null); if (previewHandle -1) { uint errCode NET_DVR_GetLastError(); MessageBox.Show(预览失败错误码 errCode); }这段代码里的 dwStreamType 决定你用主码流还是子码流。主码流分辨率高适合本地存储子码流分辨率低适合网络传输或同时多路预览。如果设备带宽有限同时预览多路摄像头时把主码流换成子码流会明显流畅。dwLinkMode 决定连接方式TCP 稳定UDP 延迟低但可能丢包。hPlayWnd 传的是 pictureBox 的句柄SDK 会在该控件的窗口区域内直接绘制视频这意味着 pictureBox 的尺寸变化后需要重新调整显示区域。预览还有一个容易被忽略的问题如果你在预览的同时想抓图抓图函数抓的是 SDK 内部的解码缓存而不是实时窗口的画面。所以抓图前要先确认预览已经稳定运行了几秒钟否则可能抓到黑帧。这些细节在 Demo 的注释里不一定有但实际开发中确实是高频踩坑点。预览跑通以后这套 Demo 的核心价值已经发挥了一半。接下来我们专门用一个章节来复盘我在拆这套 Demo 时遇到的各类问题这些问题在别的项目里同样会出现。4. 排查与避坑海康 C# 开发中五个典型问题的解决记录这部分写得比较细因为每一条都是真实踩过的坑。现象、原因、解决一条条对照着看。4.1 现象运行时报“无法加载 DLL HCNetSDK.dll”或“找不到指定的模块”这个问题出现在首次运行 Demo 时。我遇到的情况是Dll 文件明明放在 exe 同目录了却依然报错。原因有两个层面。第一HCNetSDK.dll 依赖了同目录下的 hccoredevcfg.dll、HCPreview.dll你只拷贝了主库而漏掉了依赖库就会报找不到指定模块注意不是找不到 HCNetSDK.dll 本身而是它加载时找不到依赖项。第二x86 和 x64 的 Dll 放混了。解决的套路是这样的# 把整个 SDK 运行库目录完整拷贝到 exe 输出目录 # 包含但不限于以下文件建议直接从 Demo 自带的 Dll 目录整体复制 HCNetSDK.dll HCNetSDKCom.dll PlayCtrl.dll HCCoreDevCfg.dll HCPreview.dll HCAlarm.dll AudioRender.dll hlog.dll hpr.dll复制完以后用 Dependency Walker 或 Process Explorer 检查一下有没有加载失败的模块确认位数也正确。从那以后我养成一个习惯每次换机器换项目先做一次 Dll 完整性检查而不是光看报错信息去猜。4.2 现象登录成功但预览窗口黑屏偶尔有声音这个坑非常经典。现象是登录成功、预览调用返回成功但画面是黑的。很多人在这一步反复检查代码查设备参数其实问题出在预览窗口的句柄上。海康的窗口预览机制是SDK 直接在 hPlayWnd 指定的窗口上绘制窗口必须是真实的 Win32 窗口句柄。如果你传的是 label 或者一个普通 UserControl 的句柄某些情况下绘制会失败。更隐蔽的问题是窗体有透明度属性或者 pictureBox 被其他控件遮挡都会导致画面渲染不出来。解决方法是换用双缓冲的 pictureBox并保证它没有被覆盖。另一个办法是改用回调式取流然后把视频帧画到自己的画布上但这需要自己处理解码和显示复杂度高不少。对于 Demo 里已经提供的窗口渲染方式我的建议是先确保 hPlayWnd 传的是 panel 或 pictureBox且该控件在预览期间不要执行任何重绘覆盖操作。如果预览一段时间后画面卡住往往是因为控件在 Resize 事件里没有调用 NET_DVR_RealPlay 的重新设置逻辑。4.3 现象异步回调线程里操作 UI 导致程序卡死或闪退海康的预览和报警回调都运行在 SDK 的独立线程中。如果你直接在回调函数里更新 TextBox 或 MessageBox程序轻则卡顿重则直接崩溃。这是因为 WinForm 的 UI 控件只能由创建它的线程访问跨线程访问会抛出 InvalidOperationException。这个问题在 Demo 代码里一般有意识地区分但新手改代码时很容易把逻辑直接塞进回调。解决套路是使用线程安全委托或者统一的 UI 调度器// 回调函数里返回的报警信息投递到UI线程处理 private void AlarmCallBack(int lCommand, ref NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser) { // 这里在任何线程中执行不能直接操作UI控件 string msg 报警类型代码: lCommand.ToString(); if (this.IsHandleCreated) { this.BeginInvoke(new Actionstring(UpdateAlarmUI), msg); } } private void UpdateAlarmUI(string msg) { // 这里在UI线程执行可以安全更新控件 alarmListBox.Items.Add(msg); }回调参数里的 lCommand 是报警命令类型pAlarmInfo 指向报警信息结构体dwBufLen 是信息长度。BeginInvoke 是异步投递不会阻塞回调线程适合高频报警场景。注意先判断 IsHandleCreated否则窗体关闭后回调仍然投递会导致 ObjectDisposedException。4.4 现象抓图失败错误码为 23 或 29错误码 23 在 SDK 里对应“网络错误”或“发送失败”29 对应“内存不足”类问题。我遇到的实际场景是预览正常但调用 NET_DVR_CaptureJPEGPicture 返回失败错误码 23。原因是抓图接口在部分设备固件上要求先激活通道或者触发抓图的频率太快设备来不及回应。另外这个抓图接口的参数设置也很关键// 抓图参数分辨率与质量 NET_DVR_JPEGPARA jpegPara new NET_DVR_JPEGPARA(); jpegPara.wPicSize 0; // 0表示使用设备默认分辨率 jpegPara.wPicQuality 0; // 图像质量0-20最好 bool ret NET_DVR_CaptureJPEGPicture(m_lUserID, 1, ref jpegPara, savePath); if (!ret) { uint errCode NET_DVR_GetLastError(); }这里第二个参数是通道号要和预览的通道一致。如果换了通道抓图第一次调用偶尔会失败第二次就能成功这个现象在某些固件上比较常见。官方的解决方案是抓图前设置好网络参数或者干脆连续调用两次并忽略第一次的失败。抓图保存格式是 JPG路径必须可写且不能放在需要管理员权限的目录否则 SaveFileDialog 选个 C 盘根目录就可能失败。4.5 现象程序在 64 位系统下崩溃错误显示 Access Violation这个问题有一个非常具体的场景进程是 x86但系统是 64 位而且 Dll 用的是 64 位版本混合加载导致的崩溃。错误信息里出现 c0000005也就是访问违例。排查思路很直接通过任务管理器确认进程位数通过进程模块列表确认加载的 HCNetSDK.dll 位数和进程一致。另外一个隐蔽的崩溃原因是结构体对齐。C# 和 C 的结构体内存布局在某些情况下会有差异海康的封装类里往往用了 StructLayoutAttribute 指定 LayoutKind.Sequential 来强制按声明顺序排列但如果你自己新加结构体时忘了这个特性函数返回后内存错位就会引发崩溃。所以改 Demo 代码时凡是自己新建的和 SDK 交互的结构体都要加上[StructLayout(LayoutKind.Sequential)] public struct NET_DVR_DEVICEINFO_V40 { // 字段声明 }这个特性告诉 CLR 按顺序连续排列字段不要做自动优化重排。结构体里的字符串字段要用 MarshalAs 指定非托管类型比如 [MarshalAs(UnmanagedType.ByValTStr, SizeConst 129)] 对应 C 的 char[129]。这是 C# 调用 C 动态库最经典的陷阱几乎每个项目都会遇到。5. 从预览到布防抓图、报警监听与录像下载的完整落地预览跑通只是起点。这套 Demo 的真正价值在于后续的抓图、报警和录像操作这些都是安防项目中真正被使用的功能。5.1 抓图实战JPEG 抓图与 BMP 抓图的选型差异海康提供了两种抓图接口。NET_DVR_CaptureJPEGPicture 直接抓取并保存为 JPEG 文件优点是简单一个调用完成编码和保存缺点是抓的是设备编码前的画面分辨率受设备编码参数限制。另一个接口是 NET_DVR_CapturePicture它抓取的是预览画面的当前帧根据保存路径的后缀决定格式适合抓取叠加了 OSD 信息的画面。从实际项目选型角度看如果做定时抓图存证用 JPEG 接口就够了如果做界面上的拍照按钮让用户看到画面后按下快门用 CapturePicture 更符合直觉因为抓到的就是当前看到的样子。两种接口对预览状态的要求不同JPEG 接口在部分设备上不需要实时预览也能抓图CapturePicture 则必须依赖正在进行的预览。// 用户点击拍照按钮使用当前帧抓图 bool ret NET_DVR_CapturePicture(m_lRealHandle, savePath); if (!ret) { uint err NET_DVR_GetLastError(); // 常见错误: 23网络错误, 29内存不足, 17预览不存在 }这个调用的第一个参数是预览句柄而不是用户句柄传错必然失败。海康 API 有一个规律普通操作传用户句柄 m_lUserID和实时画面相关的操作传预览句柄 m_lRealHandle。这个规律记住后接口参数基本不会用错。5.2 报警布防从布防到回调把设备事件推进业务系统设备报警是安防集成的核心功能。海康的布防流程是三个步骤先设置报警回调函数再启动布防通道最后在回调函数里根据 lCommand 分发处理。Demo 里对这个流程有完整示例。// 第一步设置报警回调 public void AlarmSetup() { NET_DVR_SetDVRMessageCallBack_V50(AlarmCallBack, IntPtr.Zero); // 第二步启动布防 channel NET_DVR_SETUPALARM_PARAM alarmParam new NET_DVR_SETUPALARM_PARAM(); alarmParam.dwSize (uint)Marshal.SizeOf(alarmParam); alarmParam.byLevel 1; // 布防等级1最高 alarmParam.byAlarmInfoType 1; // 报警信息类型扩展 m_lAlarmHandle NET_DVR_SetupAlarmChan_V41(m_lUserID, ref alarmParam); if (m_lAlarmHandle -1) { uint err NET_DVR_GetLastError(); } }布防成功后会从设备订阅运动侦测、视频遮挡、断线等事件。byLevel 参数在设备并发上报大量事件时影响事件队列的取舍策略一般置 1。byAlarmInfoType 设为 1 后回调收到的 pAlarmInfo 指向的是 NET_DVR_ALARMINFO_V40 结构体里面包含事件类型、通道号、时间戳等字段。回调里要做的事情是把事件转换成业务数据写入自己的报警记录表同时通过消息队列推给上层应用。不要在回调里做耗时的事情比如写数据库、调用远程接口回调线程被阻塞后设备会认为网络异常反而触发断线重连逻辑。5.3 录像文件下载按时间检索与远程下载参数除了实时预览和报警录像文件的下载也是高频业务。海康的录像查询流程是通过 NET_DVR_FIND_FILE_V40 按通道和时间段查找录像文件返回查找句柄然后循环调用 NET_DVR_FIND_NEXT_FILE_V40 获取下一个文件信息最后用 NET_DVR_GetDownloadPos 配合下载接口把文件拉回本地。// 按时间段查询录像文件 NET_DVR_FIND_FILE_V40 findFile new NET_DVR_FIND_FILE_V40(); findFile.lChannel 1; // 要查询的通道 findFile.dwFileType 0; // 0全部1定时录像2移动侦测录像 findFile.dwIsLocked 0; // 0未锁定1锁定 // 设置开始时间和结束时间 // 时间格式为 NET_DVR_TIME包含 year, month, day, hour, minute, second NET_DVR_TIME startTime new NET_DVR_TIME(); startTime.dwYear 2025; startTime.dwMonth 1; startTime.dwDay 1; startTime.dwHour 0; startTime.dwMinute 0; startTime.dwSecond 0; findFile.stStartTime startTime; int findHandle NET_DVR_FIND_FILE_V40(m_lUserID, ref findFile);这个查询接口一次只返回一个文件需要循环调用。查询条件中 dwFileType 的筛选特别有用如果你只关心移动侦测录像把这个字段设为 2能大大减少传输的数据量。录像文件的下载本质是远程拷贝速度受网络和录像机磁盘性能制约大文件建议放到后台线程中执行下载进度可以通过轮询 NET_DVR_GetDownloadPos 获取。6. 进阶技巧WPF 集成、RTSP 流对接与断线重连的工程经验到这里Demo 的主要内容已经全部跑通了。但如果你要把这套东西用到生产环境还有三个工程问题要处理WPF 里怎么接预览、RTSP 流怎么对接第三方播放器、设备断线后怎么办。6.1 WPF 集成HwndHost 方式还是 RTSP 方式WPF 项目里直接用 WinForm 的 PictureBox 会碰到兼容问题因为海康窗口预览渲染依赖 Win32 窗口句柄而 WPF 的控件不是传统的 HWND。常见做法是用 WindowsFormsHost 承载 pictureBox但这会导致窗口叠加和性能问题在 WPF 项目里属于勉强能用但观感一般的方案。更好的做法是走 RTSP 路径通过 NET_DVR_GetDVRConfig 拿到设备的 RTSP 端口拼接 RTSP URL例如 rtsp://admin:password192.168.1.64:554/Streaming/Channels/101然后交给 VLC.DotNet 或 LibVLC 渲染。这样画面质量、播放控制和窗口互操作都更现代化。WPF 的集成建议是商业项目用 RTSP快速 Demo 用 WindowsFormsHost。两种方案我都用过这也是目前行业里的普遍选择。6.2 断线重连与心跳保活手动实现比 SDK 自动重连更可控SDK 本身有自动重连机制但默认行为是设备恢复后 SDK 自行恢复预览这个过程中如果你是回调取流画面数据不会自动接续。我的做法是不依赖 SDK 的重连而是在业务层做心跳检测用一个定时器每隔 10 秒通过 NET_DVR_GetDVRConfig 读一次设备时间连续三次失败就判定断线执行完整的注销和重新登录流程。// 定时器里的心跳逻辑 private void HeartbeatTimer_Tick(object sender, EventArgs e) { // 读取设备时间作为心跳探测 NET_DVR_GET_TIME m_dwTime new NET_DVR_GET_TIME(); bool ret NET_DVR_GetDVRConfig(m_lUserID, NET_DVR_GET_TIME, 0, ref m_dwTime, (uint)Marshal.SizeOf(m_dwTime)); if (!ret) { m_failCount; if (m_failCount 3) { ReconnectDevice(); // 执行注销、重新登录、重新布防 m_failCount 0; } } else { m_failCount 0; } }NET_DVR_GetDVRConfig 是一个通用命令接口第二个参数传入命令码或者配置命令编号第四个参数是输出缓冲第五个参数是缓冲大小。用它做心跳探测的好处是一个接口同时验证了网络连通和设备状态而不像专门的心跳协议那样需要额外实现。重连成功后要按顺序恢复登录、布防、预览三个步骤一个都不能少。我把这套流程规范化以后在多个项目中都是这么处理的初始化时强制检查 Dll 完整性和位数匹配登录后先拉设备能力集预览启动前确认窗口句柄有效收尾时严格按照停止预览、关布防、注销、清理的顺序。这套习惯起初也是踩了不少坑才养成的特别是那几次在回调线程里直接操作 UI 导致的白屏崩溃从那以后我每写一个回调函数都会先问一句这个逻辑最终会不会碰到 UI 控件。如果会就规规矩矩走 BeginInvoke。希望这份拆解对你有点用至少能在跑通 Demo 的路上少花一个下午。本文还有配套的精品资源点击获取
返回列表