ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java调用海康威视SDK实战:PS流拉取、RTMP推流与录像下载

Java调用海康威视SDK实战:PS流拉取、RTMP推流与录像下载 简介本资源是一套基于Java语言的海康威视设备SDK二次开发实战项目面向安防监控领域Java开发者及音视频集成工程师解决网络摄像机与NVR设备在Java生态中难以高效接入、流媒体推拉、图像抓取与录像下载等核心问题。项目完整封装了实时视频流与历史回放流的推流逻辑、定时/触发式抓图、录像文件按时间范围下载、云台控制等高级功能适合作为智能安防平台、远程监看系统或AI视频分析前置模块的开发基础。压缩包共255个文件含49个核心Java源码含SDK调用封装与业务逻辑、131个XML配置与依赖声明、25个Windows平台DLL及23个Linux平台SO动态库如PlayCtrl.dll、libcrypto.so.1.0.0、SuperRender.dll等辅以YML、Shell脚本与Vue前端示例整体大小39.92MB。目前已有203人学习下载提供开箱即用的工程结构、跨平台SDK适配方案与典型调用范例显著降低海康设备Java集成门槛。1. 为什么 Java 工程师一接到「海康威视 SDK 二次开发」需求就头皮发紧这不是调个 REST API而是要亲手把 H.264 流从黑匣子里拽出来再喂给 FFmpeg你刚收到需求用 Java 对接海康威视的 IPC网络摄像机和 NVR网络硬盘录像机实现三件事——实时视频流推送到 RTMP/HTTP-FLV、按时间范围下载历史录像、任意帧抓图保存为 JPEG。你以为只是加个http://ip:8000/ISAPI/Streaming/channels/101就完事错。海康威视官方 SDKiSecureCenter SDK / HCNetSDK压根不提供标准 HTTP 接口它是一套 C/C 编写的本地动态库Windows 下是HCNetSDK.dllLinux 下是libhcnetsdk.so所有核心能力——包括登录、预览、回放、抓图、录像下载——都必须通过 JNI 调用底层 DLL/SO。Java 层只是个“调度员”真正干活的是那个你连源码都看不到的黑匣子。更现实的是你没法用 Spring Boot 写个 Controller 就返回视频流得自己开线程管理 SDK 的回调、自己拼接 PS 流、自己处理时间戳乱序、自己扛住设备断连重连。这不是写业务逻辑是写嵌入式级的音视频胶水代码。适合谁有 Java 基础、能看懂 C 头文件、愿意啃.dll文档、能调试 native crash 的后端或安防集成工程师。别指望纯 Web 开发者三天上手——这活儿血泪经验告诉我没 3 天 debugNET_DVR_RealPlay_V30回调空指针你都不算真正摸到门。2. 从零搭起 Java 调用海康威视 SDK 的最小可行环境DLL 加载、用户登录、设备在线状态校验2.1 下载与部署 SDK 包只认官方渠道拒绝任何“精简版”或“免注册版”海康威视 SDK 官方下载入口在 https://www.hikvision.com/cn/support/download/sdk/ 注意必须用企业邮箱注册个人邮箱常被拒。你需要下载“iSecureCenter SDK for Windows/Linux”不是“Web SDK”或“ISAPI SDK”版本建议选v6.1.9.45 或 v6.1.10.47这两个版本对 Java JNI 兼容性最稳v6.2 开始强制要求 .NET Framework 4.7.2Java 项目易踩坑。解压后你会看到HCNetSDK.dllWin64、libhcnetsdk.soLinux x64PlayCtrl.dllWin 下用于本地解码播放Java 不直接用但依赖其导出函数HCNetSDK.jar官方提供的 Java 封装层仅含接口定义无实现Doc/目录下的HCNetSDK编程指南.pdf和HCNetSDK结构体说明.pdf提示HCNetSDK.jar不能直接mvn install—— 它只是 Java 接口桩实际逻辑全在 DLL/SO 里。你必须把HCNetSDK.dll放进java.library.path路径如System.getProperty(java.library.path)输出的目录或用System.setProperty(jna.library.path, D:/sdk/lib);显式指定。Linux 下确保LD_LIBRARY_PATH包含libhcnetsdk.so所在目录并chmod x libhcnetsdk.so。2.2 初始化 SDK 并完成设备登录四步不可省缺一即失败海康威视 SDK 是典型的“先初始化、再登录、后操作”状态机。跳过任何一步后续所有 API 都返回-1失败。以下是 Java 层最简登录代码基于 JNA 封装非官方 HCNetSDK.jar// 使用 JNA 自定义映射比官方 jar 更可控 public interface HCNetSDK extends Library { HCNetSDK INSTANCE Native.load(HCNetSDK, HCNetSDK.class); // 初始化 SDK必须最先调用 boolean NET_DVR_Init(); // 设置 SDK 日志路径关键否则崩溃无日志 boolean NET_DVR_SetLogToFile(int level, String sLogPath, boolean bAutoDel); // 用户登录返回 long 类型的用户句柄-1 表示失败 int NET_DVR_Login_V30(String sIP, int wPort, String sUserName, String sPassword, NET_DVR_DEVICEINFO_V30 lpDeviceInfo); // 设备信息结构体必须 new 出来并传引用 class NET_DVR_DEVICEINFO_V30 extends Structure { public byte[] sSerialNumber new byte[48]; public byte byChannelNum; public byte byStartChan; public byte byIPChanNum; public byte byZeroChanNum; public byte[] sDeviceVersion new byte[16]; // ... 其他字段省略完整见 HCNetSDK结构体说明.pdf Override protected ListString getFieldOrder() { return Arrays.asList(sSerialNumber, byChannelNum, byStartChan, byIPChanNum, byZeroChanNum, sDeviceVersion); } } } // 登录主流程 public class HikLoginManager { private static int userId -1; public static boolean initAndLogin(String ip, int port, String user, String pwd) { // Step 1: 初始化 SDK if (!HCNetSDK.INSTANCE.NET_DVR_Init()) { System.err.println(SDK 初始化失败请检查 DLL 是否加载成功); return false; } // Step 2: 设置日志强烈建议默认日志在 C:\Program Files\Hikvision\SDK\log HCNetSDK.INSTANCE.NET_DVR_SetLogToFile(3, D:/hik-log/, true); // 3DEBUG 级别 // Step 3: 构造设备信息对象 HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo new HCNetSDK.NET_DVR_DEVICEINFO_V30(); // Step 4: 登录注意端口必须是设备 Web 服务端口通常是 8000不是 RTSP 端口 554 userId HCNetSDK.INSTANCE.NET_DVR_Login_V30(ip, port, user, pwd, deviceInfo); if (userId -1) { int errCode HCNetSDK.INSTANCE.NET_DVR_GetLastError(); System.err.println(登录失败错误码 errCode 请查 HCNetSDK 错误码表); return false; } System.out.println(登录成功设备通道数 deviceInfo.byChannelNum); return true; } }参数说明与逻辑要点NET_DVR_Init()必须全局只调一次通常放在static {}块中。多次调用不会报错但浪费资源。NET_DVR_SetLogToFile(3, ..., true)中level3表示输出 DEBUG 日志含每帧时间戳、回调触发详情这是排查流中断、花屏的唯一依据。日志路径必须是已存在且可写的绝对路径相对路径会静默失败。NET_DVR_Login_V30的port参数是设备的Web 服务端口默认 8000不是 RTSP 端口554或 ONVIF 端口80。若设备改过 Web 端口此处必须同步。deviceInfo必须是new出来的实例JNA 会自动将其内存地址传给 DLL。传null或未初始化字段会导致 DLL 访问非法内存而崩溃黑屏、Java 进程退出。2.3 设备在线状态心跳检测别等NET_DVR_Logout才发现设备已掉线海康威视设备在局域网内可能因 IP 冲突、交换机故障、电源波动瞬间离线。SDK 不会主动通知你userId句柄仍有效但后续RealPlay会卡死或回调不触发。必须自己实现心跳// 每 30 秒 ping 一次设备 TCP 端口8000 public static boolean isDeviceOnline(String ip, int port) { try (Socket socket new Socket()) { socket.connect(new InetSocketAddress(ip, port), 2000); // 2秒超时 return true; } catch (IOException e) { return false; } } // 后台线程定时检测示例 ScheduledExecutorService heartBeat Executors.newSingleThreadScheduledExecutor(); heartBeat.scheduleAtFixedRate(() - { if (!isDeviceOnline(192.168.1.64, 8000)) { System.err.println(设备 192.168.1.64 已离线执行自动登出); if (userId ! -1) { HCNetSDK.INSTANCE.NET_DVR_Logout(userId); userId -1; } // 触发重连逻辑... } }, 0, 30, TimeUnit.SECONDS);注意不能依赖NET_DVR_GetDVRState该 API 在 v6.1.x 中对 NVR 返回不稳定TCP 端口探测是最可靠方式。实测发现设备 Web 服务端口通基本意味着 SDK 通信可用。3. 实时流拉取与推流用NET_DVR_RealPlay_V30获取 PS 流再用 FFmpeg 转封装为 RTMP3.1 启动实时预览回调函数接收原始 PS 流不是 JPEG 或 H.264 Annex B海康威视 IPC/NVR 输出的是MPEG-4 Part 2 / H.264 PS 流Program Stream不是常见的 Annex B 格式00 00 00 01 分隔。PS 流包含系统头、PES 包、时间戳需完整解析才能喂给 FFmpeg。NET_DVR_RealPlay_V30的核心是设置一个fRealDataCallBack回调函数SDK 每收到一帧数据就调用它// 定义回调函数JNA 方式 public interface RealDataCallback extends StdCallLibrary.StdCallCallback { void callback(int nRealHandle, int nDataType, byte[] pBuffer, int dwBufSize, Object pUser); } // 启动预览关键nMode0 表示 TCP 主动连接nMode1 表示 UDP推荐 TCP public static int startRealPlay(int userId, int channel, RealDataCallback callback) { HCNetSDK.NET_DVR_PREVIEWINFO previewInfo new HCNetSDK.NET_DVR_PREVIEWINFO(); previewInfo.hPlayHandle null; // 预留SDK 内部填充 previewInfo.lChannel channel; // 通道号IPC 从 1 开始NVR 从 1 开始不是 0 previewInfo.dwStreamType 1; // 0主码流1子码流节省带宽 previewInfo.dwLinkMode 0; // 0TCP1UDPUDP 易丢包慎用 previewInfo.bBlocked true; // true阻塞模式false非阻塞推荐 true避免回调堆积 // 注册回调pUser 可传自定义对象如 OutputStream return HCNetSDK.INSTANCE.NET_DVR_RealPlay_V30(userId, previewInfo, callback, null, true); } // 回调实现将 PS 流写入内存队列或文件 RealDataCallback realDataCB (nRealHandle, nDataType, pBuffer, dwBufSize, pUser) - { if (nDataType HCNetSDK.NET_DVR_SYSHEAD) { // 系统头仅首次出现 System.out.println(收到 PS 系统头长度 dwBufSize); // 通常此处不做处理FFmpeg 会自动识别 } else if (nDataType HCNetSDK.NET_DVR_STREAMDATA) { // 实际视频流数据 // pBuffer 是 PS 流原始字节直接写入 ByteArrayOutputStream 或 RingBuffer if (psOutputStream ! null) { try { psOutputStream.write(pBuffer, 0, dwBufSize); } catch (IOException e) { e.printStackTrace(); } } } };关键参数解释dwStreamType1务必设为子码流Sub-stream。主码流0分辨率高、码率大如 4K8MbpsJava 线程处理不过来极易导致回调堆积、内存 OOM。子码流如 720P1Mbps足够用于 Web 推流。dwLinkMode0TCP 模式保证数据不丢但延迟略高100~300msUDP 模式延迟低但丢包率高需自行重传生产环境禁用 UDP。bBlockedtrue阻塞模式下SDK 会等待你的回调函数返回后再推送下一帧避免缓冲区溢出非阻塞模式下 SDK 可能因 Java 回调慢而丢帧。3.2 将 PS 流实时推送到 RTMP用 FFmpeg 进程管道注入而非 Java 解码你绝不能在 Java 里解析 PS 流再转 H.264 Annex B——计算量巨大GC 频繁10 路流就吃满 CPU。正确做法把 PS 流当作二进制管道输入交给本地 FFmpeg 进程实时转封装。FFmpeg 命令如下# 从 stdin 读取 PS 流转为 RTMP 推送关键参数说明 ffmpeg -f mpegts -i - \ -c:v copy -c:a aac -ar 44100 -ab 64k \ -f flv rtmp://192.168.1.100/live/camera1-f mpegts -i -强制 FFmpeg 将 stdin 当作 MPEG-TS 流海康 PS 流兼容 TS 解析器-c:v copy关键不解码不重编码直接拷贝视频流零延迟、零 CPU-c:a aac音频流转 AAC海康设备若无音频此参数可删-f flv输出格式为 FLVRTMP 协议要求Java 调用代码使用ProcessBuilderpublic class FfmpegPusher { private Process ffmpegProcess; private OutputStream ffmpegStdin; public void startPush(String rtmpUrl) throws IOException { // 启动 FFmpeg 进程 ProcessBuilder pb new ProcessBuilder( ffmpeg, -f, mpegts, -i, -, -c:v, copy, -c:a, aac, -ar, 44100, -ab, 64k, -f, flv, rtmpUrl ); pb.redirectError(new File(D:/ffmpeg-error.log)); // 捕获 FFmpeg 错误日志 ffmpegProcess pb.start(); ffmpegStdin ffmpegProcess.getOutputStream(); // 启动线程持续将 PS 流写入 FFmpeg stdin Thread pushThread new Thread(() - { try { byte[] buffer new byte[65536]; int len; while ((len psInputStream.read(buffer)) ! -1) { ffmpegStdin.write(buffer, 0, len); ffmpegStdin.flush(); // 必须 flush否则 FFmpeg 缓存不触发 } } catch (IOException e) { e.printStackTrace(); } }); pushThread.setDaemon(true); pushThread.start(); } public void stop() { if (ffmpegProcess ! null ffmpegProcess.isAlive()) { ffmpegProcess.destroy(); } } }提示ffmpegStdin.flush()是血泪经验——不 flushFFmpeg 会一直等满 64KB 缓存才处理导致首帧延迟 3~5 秒。每写一次write()就flush()一次。4. 历史录像下载与抓图用NET_DVR_PlayBackByTime_V40定位文件NET_DVR_SaveRealData保存为 AVI4.1 按时间范围搜索录像文件NET_DVR_FindNextFile循环遍历不是 SQL 查询海康 SDK 不提供“查询 2024-05-01 08:00 到 09:00 的录像”这种高级 API。你必须调用NET_DVR_FindFirstFile_V40指定起止时间、通道号、类型录像/图片获取第一个文件句柄循环调用NET_DVR_FindNextFile获取后续文件直到返回false对每个文件调用NET_DVR_PlayBackByTime_V40启动回放再用NET_DVR_SaveRealData保存为本地 AVI// 搜索录像文件返回文件信息列表 public ListHCNetSDK.NET_DVR_FINDDATA_V40 searchRecordFiles( int userId, int channel, Date startTime, Date endTime) { ListHCNetSDK.NET_DVR_FINDDATA_V40 files new ArrayList(); HCNetSDK.NET_DVR_CONDITION_V40 condition new HCNetSDK.NET_DVR_CONDITION_V40(); condition.struStartTime.dwYear startTime.getYear() 1900; condition.struStartTime.dwMonth startTime.getMonth() 1; condition.struStartTime.dwDay startTime.getDate(); condition.struStartTime.dwHour startTime.getHours(); condition.struStartTime.dwMinute startTime.getMinutes(); condition.struStartTime.dwSecond startTime.getSeconds(); condition.struStopTime.dwYear endTime.getYear() 1900; condition.struStopTime.dwMonth endTime.getMonth() 1; condition.struStopTime.dwDay endTime.getDate(); condition.struStopTime.dwHour endTime.getHours(); condition.struStopTime.dwMinute endTime.getMinutes(); condition.struStopTime.dwSecond endTime.getSeconds(); condition.dwChannel channel; condition.dwFileType 0x01; // 0x01普通录像0x02移动侦测0x04报警录像 IntByReference lFindHandle new IntByReference(); HCNetSDK.NET_DVR_FINDDATA_V40 findData new HCNetSDK.NET_DVR_FINDDATA_V40(); // 第一次查找 int handle HCNetSDK.INSTANCE.NET_DVR_FindFirstFile_V40(userId, condition, lFindHandle); if (handle -1) { System.err.println(查找录像失败错误码 HCNetSDK.INSTANCE.NET_DVR_GetLastError()); return files; } // 循环获取所有匹配文件 do { if (HCNetSDK.INSTANCE.NET_DVR_FindNextFile(lFindHandle.getValue(), findData)) { files.add(findData.clone()); // clone 避免内存复用 } else { break; } } while (true); HCNetSDK.INSTANCE.NET_DVR_FindClose(lFindHandle.getValue()); return files; }注意点dwFileType是位掩码如需同时查普通录像和报警录像应设为0x01 | 0x04。findData结构体中的struStartTime/struStopTime是该录像文件的实际起止时间不是你搜索的时间范围。一个 1 小时的录像文件可能跨你搜索的两个时间段。4.2 下载单个录像文件启动回放 → 保存为 AVI → 关闭回放三步缺一不可// 下载单个录像文件AVI 格式 public boolean downloadRecordFile(int userId, HCNetSDK.NET_DVR_FINDDATA_V40 file, String outputPath) { // Step 1: 启动回放注意lPlayHandle 是回放句柄非预览句柄 HCNetSDK.NET_DVR_PLAYBACK_INFO_V40 playInfo new HCNetSDK.NET_DVR_PLAYBACK_INFO_V40(); playInfo.struStartTime file.struStartTime; playInfo.struStopTime file.struStopTime; playInfo.dwChannel file.dwChannel; playInfo.dwStreamType 0; // 0主码流1子码流下载录像建议用主码流 IntByReference lPlayHandle new IntByReference(); if (!HCNetSDK.INSTANCE.NET_DVR_PlayBackByTime_V40(userId, playInfo, lPlayHandle)) { System.err.println(启动回放失败 HCNetSDK.INSTANCE.NET_DVR_GetLastError()); return false; } // Step 2: 开始保存outputPath 必须是 .avi 后缀 if (!HCNetSDK.INSTANCE.NET_DVR_SaveRealData(lPlayHandle.getValue(), outputPath)) { System.err.println(开始保存失败 HCNetSDK.INSTANCE.NET_DVR_GetLastError()); HCNetSDK.INSTANCE.NET_DVR_StopPlayBack(lPlayHandle.getValue()); return false; } // Step 3: 等待保存完成轮询状态 int status; do { status HCNetSDK.INSTANCE.NET_DVR_GetPlayBackPos(lPlayHandle.getValue()); System.out.println(当前进度 status %); try { Thread.sleep(1000); } catch (InterruptedException e) {} } while (status 100); // Step 4: 停止回放 HCNetSDK.INSTANCE.NET_DVR_StopPlayBack(lPlayHandle.getValue()); System.out.println(录像下载完成 outputPath); return true; }提示NET_DVR_SaveRealData保存的是 AVI 封装内部视频流仍是 H.264 PS 格式。若需 MP4需用 FFmpeg 转换ffmpeg -i input.avi -c copy output.mp4无损转换秒级完成。4.3 抓图功能NET_DVR_CaptureJPEGPicture生成 JPEG不是截图抓图不是截取当前播放画面而是向设备发送指令让 IPC/NVR硬件编码一张 JPEG 图片并返回质量高、速度快、不依赖预览状态// 抓图立即触发无需预览 public boolean captureJpeg(int userId, int channel, String outputPath) { // 参数用户句柄、通道号、抓图质量0~63推荐 50、输出路径 boolean result HCNetSDK.INSTANCE.NET_DVR_CaptureJPEGPicture( userId, channel, 50, outputPath ); if (!result) { int err HCNetSDK.INSTANCE.NET_DVR_GetLastError(); System.err.println(抓图失败错误码 err); return false; } System.out.println(抓图成功 outputPath); return true; }避坑点outputPath必须是绝对路径且目录必须存在。D:/pic.jpg可以./pic.jpg会失败。抓图质量wQuality50是平衡点低于 30 图片模糊高于 60 文件过大单张 2MB。此 API 不需要设备正在预览只要登录成功即可调用适合做定时快照任务。5. 避坑Java 调用海康 SDK 的 5 个真实翻车现场与解法5.1 现象Java 进程突然退出控制台无任何异常堆栈原因SDK DLL 内部发生访问违规Access Violation如传入了已被 GC 回收的Structure对象指针或回调函数中抛出了未捕获的 Java 异常JNA 默认会终止进程。解决所有Structure对象如NET_DVR_DEVICEINFO_V30必须在回调函数外new并在整个生命周期内保持强引用不要让 GC 回收回调函数内try-catch包裹全部逻辑绝不让异常透出到 JNA 层启用 SDK 日志NET_DVR_SetLogToFile日志中会记录EXCEPTION_ACCESS_VIOLATION及内存地址结合windbg分析Windows或gdbLinux。5.2 现象NET_DVR_RealPlay_V30返回 0但回调函数从不触发原因最常见于dwLinkMode设为 1UDP且防火墙拦截了 UDP 端口或设备 UDP 功能未开启其次是bBlockedfalse导致 SDK 回调队列满后静默丢弃。解决统一使用dwLinkMode0TCP检查设备 Web 界面配置 → 网络 → 高级配置 → TCP/UDP 设置 → 确保“TCP 主动连接”启用bBlockedtrue并确保回调函数执行时间 50ms避免阻塞 SDK 线程。5.3 现象推流到 Nginx-RTMP 后播放器显示“无法连接”或“黑屏”原因FFmpeg 输入格式识别失败。海康 PS 流头部缺少标准 MPEG-TS sync 字节0x47FFmpeg 默认按 TS 解析失败。解决强制指定输入格式为mpegps不是mpegtsffmpeg -f mpegps -i - -c:v copy -c:a aac -f flv rtmp://...或添加-analyzeduration 2000000延长分析时间不推荐增加首帧延迟。5.4 现象NET_DVR_FindFirstFile_V40总是返回 0 个文件但设备确有录像原因时间参数未对齐设备时区。海康设备默认使用本地时区如东八区而 JavaDate对象是 UTC 时间戳直接赋值会导致时间偏移 8 小时。解决不要用date.getYear()等过时方法改用Calendar或LocalDateTime转换Calendar cal Calendar.getInstance(TimeZone.getTimeZone(GMT8)); cal.setTime(startTime); condition.struStartTime.dwYear cal.get(Calendar.YEAR); condition.struStartTime.dwMonth cal.get(Calendar.MONTH) 1; // ... 其他字段同理5.5 现象多路预览时某一路回调停止其他路正常原因单个userId句柄的并发预览路数受限。海康 SDK 默认限制为8 路IPC或16 路NVR超出后新RealPlay返回 0旧路不受影响。解决查阅设备规格书确认最大预览路数超过限制时必须创建多个用户登录句柄即多次NET_DVR_Login_V30每 8 路分配一个userId切勿复用同一userId启动超过限制的预览SDK 不报错但行为不可预测。6. 进阶技巧用NET_DVR_SetDVRMessage实现设备事件订阅替代轮询告警你是否还在每秒调用NET_DVR_GetAlarmQueue检查是否有移动侦测太低效。海康 SDK 提供异步事件机制NET_DVR_SetDVRMessage注册一个 Windows 消息回调Windows或信号量Linux当设备产生报警移动侦测、遮挡、硬盘满SDK 会主动向你的 Java 程序发送消息。6.1 Windows 下用 JNA Hook Windows 消息循环Java 本身无消息循环需借助User32库创建隐藏窗口并监听public class AlarmMessageHandler { private static final int WM_USER 0x0400; private static final int MSG_ALARM WM_USER 1001; // 定义窗口过程回调 public interface WndProc extends StdCallLibrary.StdCallCallback { LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam); } // 创建窗口并注册消息 public static HWND createAlarmWindow(WndProc proc) { WinDef.HINSTANCE hInstance User32.INSTANCE.GetModuleHandle(null); HWND hwnd User32.INSTANCE.CreateWindowEx( 0, STATIC, , 0, 0, 0, 0, 0, null, null, hInstance, null ); User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, proc); return hwnd; } // SDK 事件回调在 WndProc 中触发 public static void onAlarmEvent(int nCommand, HCNetSDK.NET_DVR_ALARMINFO lpAlarmInfo) { if (nCommand HCNetSDK.NET_DVR_ALARM_PIC || nCommand HCNetSDK.NET_DVR_ALARM_MOTION) { System.out.println(收到移动侦测报警通道 lpAlarmInfo.dwChannel); // 触发抓图、推流、通知等业务逻辑 } } } // 启动时注册 HWND hwnd AlarmMessageHandler.createAlarmWindow((hWnd, uMsg, wParam, lParam) - { if (uMsg AlarmMessageHandler.MSG_ALARM) { // 解析 lParam 中的报警数据需根据 SDK 文档反序列化 AlarmMessageHandler.onAlarmEvent(...); } return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam); }); // 向 SDK 注册消息 HCNetSDK.INSTANCE.NET_DVR_SetDVRMessage(hwnd, AlarmMessageHandler.MSG_ALARM);6.2 Linux 下用信号量替代sigwaitLinux 无窗口消息改用sigwait等待 SIGUSR1// C 侧注册信号处理需编译为 .so 供 Java 调用 void setup_alarm_signal() { sigset_t set; sigemptyset(set); sigaddset(set, SIGUSR1); pthread_sigmask(SIG_BLOCK, set, NULL); }Java 层用Runtime.getRuntime().exec(kill -USR1 pid)发送信号或由 SDK 内部触发需 SDK 支持。6.3 为什么值得投入事件驱动 vs 轮询的性能对比场景轮询方案事件驱动方案优势100 路设备报警检测每秒 100 次NET_DVR_GetAlarmQueueCPU 占用 35%仅在报警发生时触发回调CPU 占用 2%降低 95% CPU 开销报警响应延迟500ms ~ 1s取决于轮询间隔 50msSDK 内部立即投递实时性提升 20 倍代码复杂度需维护线程池、超时重试、状态机单回调函数处理无状态管理减少 70% 告警模块代码我上线过一个 200 路 NVR 的集中管理平台切换事件驱动后告警服务从 4C8G 降配到 2C4G且再没出现过漏报。这件事教会我海康 SDK 的价值不在“能做什么”而在“怎么让它少做”——少一次轮询就少一分不可靠。希望帮到你。本文还有配套的精品资源点击获取
返回列表