资讯动态

Java海康SDK二次开发:从取流到推流的全链路实战

发布时间:2026/10/2 8:32:38 来源:尧图企业网站定制
简介面向Java开发者的海康威视网络摄像机与NVR二次开发资源包以实时流/历史流推流、抓图、录像下载、云台控制四大功能为主线提供在Windows/Linux环境可直接运行和扩展的项目代码。压缩包共256个文件约39.25MB其中包含131个XML配置、49个Java源码、25个DLL、23个SO动态库及3个JAR依赖包另有Vue前端、Shell脚本和SQL脚本覆盖后端驱动加载、前端预览界面与数据库存储等环节。包内文件按功能模块组织便于针对实时预览、录像回放、抓图存档、云台操作等场景快速定位对应代码。结合SDK接口说明可重点学习RTSP/HLS推流、录像检索、多线程下载、断点续传、云台协议适配以及Java与原生动态库交互等实现细节。其中还涉及SDK库的加载路径配置、日志记录与版本兼容性处理能帮助开发者避开常见环境坑点。已有1163人下载学习适合安防系统集成、视频平台对接以及需要独立完成设备二次开发的Java工程师参考。1. Java海康威视SDK二次开发先把推流链路定下来做 Java 海康威视 SDK 二次开发的人十有八九不是冲着研究安防协议来的而是被一句需求推着走公司买了一批网络摄像机和 NVR 录像机要把它们变成业务系统里的实时预览、历史回放还要能抓图、能下载录像。海康 HCNetSDK 本身是 C 语言接口Java 侧只能通过 JNA 或 JNI 去调用真正跑起来你会发现登录设备并不难难的是把实时流和历史流稳定推出去——推流中断、通道号写错、浏览器插件装不上每一步都在消耗排期。这篇按我落过地的链路讲SDK 初始化、登录取流、转推 RTMP/SRS、历史流回放与录像下载最后是避坑和存活验证。适合正在做安防集成、视频上云或运维平台的 Java 后端参考。2. 海康SDK二次开发的前置条件JNA加载、设备登录与取流通道选择2.1 先分清直连、NVR转发与RTSP裸流选错方向后面全返工网络摄像机自带 RTSP 服务NVR 本质上是一个录像加转发网关。很多人第一反应是既然有 RTSP 地址直接让播放器拉流不就行了如果只是看单路画面确实可以。可一旦业务变成“从 NVR 取某一段历史录像、控制倍速播放、抓图、下载证据”纯 RTSP 地址就绕不过去了——历史流的 URL 参数、时间范围和权限控制都很脆弱。SDK 的角色是给你一个稳定可控的登录态和会话句柄把设备能力暴露给上层。我在实际项目里见到的取流方式有三种选错方向后面基本要返工方式典型路径适用场景直接 RTSP 裸流rtsp://user:passip:554/Streaming/Channels/101单路实时预览、临时调试NVR 转发取流通过 NVR 取通道一路的流多路摄像头统一出口、集中布防SDK 登录后取流Java 调 HCNetSDK 登录再做控制历史回放、抓图、录像下载、设备状态有人在项目里习惯让 NVR 先把录像定期转存到 NAS再从 NAS 把文件拖回来。这个方案测试环境没问题生产上会被 NAS 协议兼容性、录像文件切分粒度拖后腿。绕过 SDK 等于把调度粒度限制在文件名级别想按“今天上午 9 点到 10 点”取一段录像会非常别扭。NAS 适合做冷备不适合做业务取流。2.2 在Linux/Windows下用JNA加载HCNetSDK库文件放置与登录最小代码HCNetSDK 在 Windows 下是HCNetSDK.dll在 Linux 下是libhcnetsdk.so并且带一大批依赖库不能只拷一个主库过去。我的做法是建一个专用目录比如/opt/hik/libs把 SDK 发布包里的 so 和 dll、依赖库都放进去启动时通过 JNA 的jna.library.path指定这个目录。import com.sun.jna.Library; import com.sun.jna.Native; public interface HCNetSDK extends Library { HCNetSDK INSTANCE Native.load(hcnetsdk, HCNetSDK.class); }上面这段只是加载库真正的难点是登录结构体。JNA 的Structure要求字段顺序和 C 结构体严格对齐不同 SDK 版本的结构体会多出保留字段抄网上的代码前一定要对着 SDK 头文件核对字段。public class NET_DVR_LOGIN_INFO extends Structure { public int dwSize; public byte[] sDeviceAddress new byte[129]; public byte[] sLoginName new byte[128]; public byte[] sPassword new byte[64]; public short wPort; // 实际还有 byUseTransport、cbLoginResult 等字段这里省略 } public class NET_DVR_DEVICEINFO_V40 extends Structure { public byte byChanNum; public byte byIPChanNum; // 省略其他字段 }登录调用NET_DVR_LOGIN_INFO loginInfo new NET_DVR_LOGIN_INFO(); loginInfo.writeString(sDeviceAddress, 192.168.1.64); loginInfo.writeString(sLoginName, admin); loginInfo.writeString(sPassword, yourPassword); loginInfo.wPort 8000; loginInfo.dwSize loginInfo.size(); NET_DVR_DEVICEINFO_V40 deviceInfo new NET_DVR_DEVICEINFO_V40(); int userId HCNetSDK.INSTANCE.NET_DVR_Login_V40(loginInfo, deviceInfo); if (userId 0) { int err HCNetSDK.INSTANCE.NET_DVR_GetLastError(); System.err.println(登录失败SDK错误码 err); return; }这段代码的逻辑是NET_DVR_Login_V40成功返回用户 ID小于 0 就查错误码。地址不要带http://端口默认 8000密码不能直接拼在 URL 里。错误码里 17、23、29 是最常见的几种分别对应账号密码错误、通道不可用、权限不足真遇到报错先查错误码表别瞎改参数。2.3 从设备信息里拿通道数NVR通道号别写死很多 NVR 挂了多个摄像头通道号既有数字通道又有 IP 通道。设备返回的byChanNum和byIPChanNum含义不同前者是数字通道数后者是 IP 通道数具体布局和 NVR 型号强相关。如果只取一路可以写死 1但要搭平台就必须把这些字段存到数据库后续所有取流地址从库里读。int channelCount deviceInfo.byChanNum; System.out.println(设备数字通道数 channelCount); if (deviceInfo.byIPChanNum 0) { System.out.println(IP通道数 deviceInfo.byIPChanNum); }海康录像机的命名规则在这个环节最容易坑人。RTSP 路径里的101不是随便写的第一位是通道号第二位固定是 0第三位是码流类型101代表第一路主码流102代表第一路子码流。但部分 NVR 固件的 IP 通道编号和设备面板上的物理通道不一致拼地址前必须用 SDK 通道列表或设备网页端确认。用 VLC 试地址时返回 401 不一定是密码错很可能是取流路径不对这是新手最容易翻车的地方。另外登录成功后要记得初始化 SDKJVM 退出时调用NET_DVR_Cleanup。很多人漏了这一步连续登录几次后句柄泄露服务只能重启。我把初始化放到 Spring 的PostConstruct退出清理放到PreDestroy这是最基本的卫生习惯。3. 实时流推流从RTSP拉流到RTMP/SRS参数和进程守护一次讲清3.1 为什么不建议Java直接读码流推流链路的取舍海康 SDK 提供实时流回调NET_DVR_RealPlay_V40Java 可以在回调里拿到码流包再用 JavaCV 的FFmpegFrameRecorder封装推流。这条链路看起来干净纯 JVM 语言搞定一切但我用下来的结论是实时流超过 4 路就不要再硬撑了。码流回调的线程模型、GC 停顿和 Native 内存释放很容易让服务看起来活着但没有画面排查时要同时看 JVM dump 和 SDK 日志成本很高。我一般把链路分成两截Java 只负责 SDK 登录、生成地址、管理并拉起 ffmpeg 进程ffmpeg 负责拉流转推。好处是 ffmpeg 进程崩溃不会拖垮 JVM可以单独重启坏处是要多维护一套进程生命周期但这点复杂度换稳定性完全值得。推流服务本来就是长时间跑的东西宁可进程边界清晰一点。3.2 用SDK登录后拼RTSP地址再交给ffmpeg推到RTMP登录成功的设备可以拼出实时流的 RTSP 地址常见格式是rtsp://admin:yourPassword192.168.1.64:554/Streaming/Channels/101密码带特殊字符时直接拼接字符串会让 URL 解析出错建议用URLEncoder处理后再拼。然后启动一个 ffmpeg 子进程ffmpeg -rtsp_transport tcp -stimeout 5000000 \ -i rtsp://admin:yourPassword192.168.1.64:554/Streaming/Channels/101 \ -c:v copy -c:a aac -b:a 128k \ -f flv rtmp://192.168.1.10:1935/live/camera_101如果源设备输出 H.265而目标播放器或 FLV 封装不支持 H.265就不能用-c:v copy要改成转码-c:v libx264 -preset veryfast -g 25。H.264 源可以直接 copyCPU 最低。Java 侧启动进程要小心管道阻塞ListString cmd new ArrayList(); cmd.add(ffmpeg); cmd.add(-rtsp_transport); cmd.add(tcp); cmd.add(-stimeout); cmd.add(5000000); cmd.add(-i); cmd.add(rtspUrl); cmd.add(-c:v); cmd.add(copy); cmd.add(-c:a); cmd.add(aac); cmd.add(-b:a); cmd.add(128k); cmd.add(-f); cmd.add(flv); cmd.add(rtmpUrl); ProcessBuilder pb new ProcessBuilder(cmd); pb.redirectErrorStream(true); pb.redirectOutput(new File(/logs/ffmpeg_ channelId .log)); Process proc pb.start();redirectErrorStream(true)很关键不然 ffmpeg 的 stderr 会积满管道导致进程阻塞。日志写到文件方便事后确认是不是因为设备断流退出。这样做了之后ffmpeg 的退出原因基本一目了然。3.3 关键参数表TCP传输、超时、GOP和编码器选择参数推荐值作用-rtsp_transporttcp用 TCP 拉 RTSP避免 UDP 丢包花屏-stimeout5000000底层 socket 超时 5 秒源端断流能快速退出-c:v copy源为 H.264不转码视频CPU 最低-c:v libx264 -preset veryfast -g 25源为 H.265 或目标兼容差转 H.264GOP 固定 25 帧-c:a aac -b:a 128k源音频为 G.711转成 FLV/RTMP 标准音频-f flv推 RTMP封装格式参数别一起堆上去。先判断源编码再决定要不要转码。判断源编码用ffprobe -show_streams看一眼或者在海康后台把主码流编码直接改成 H.264这是让 SRS 推流不稳定问题少一半的土办法。很多人发现 SRS 推流不稳定就怀疑 SRS 配置实际上源头丢帧、GOP 太大、RTSP 用 UDP 传输才是主因。我在调试时会把设备端主码流改成 H.264码率限制 4Mbps帧率 25I 帧间隔 50。这样即使 ffmpeg 用 copy 推关键帧间隔也在播放器可接受范围内。断线重连不要无脑无限重启while (!Thread.currentThread().isInterrupted()) { Process proc pb.start(); int exitCode proc.waitFor(); if (exitCode ! 0) { log.error(channel {} ffmpeg exited with {}, channelId, exitCode); Thread.sleep(3000); continue; } // 正常退出可能是手动停止 break; }退出码 0 也可能是主动停止非 0 最常见的是 139段错误和 234源断开。连续失败次数超过 5 次就发告警避免把流媒体服务器和服务日志同时打爆。4. 历史流回放推流与录像下载按时间定位、控制播放、抓图存证4.1 历史流取流RTSP带时间参数与SDK按时间回放两条路历史流和实时流的本质区别是多了时间范围。最直接的办法是用历史 RTSP 地址海康设备常见的格式是rtsp://admin:yourPassword192.168.1.64:554/Streaming/tracks/101?starttime20240401T100000Zendtime20240401T110000Zstarttime和endtime是 UTC 时间格式固定中间用T连接最后带Z。这个地址可以直接交给 ffmpeg 拉流转推命令和实时流几乎一样只是把-i参数换成这个地址。注意设备时区如果按北京时间传要先转成 UTC不然回放的总不是你要的那一段。另一条路是 SDK 按时间回放调用NET_DVR_PlayBackByTime_V40传通道号、起止时间返回回放句柄后面的暂停、倍速、停止都在这个句柄上操作。业务上如果只是给用户一个起止时间走前一条路就够了如果要做录像文件列表展示再用NET_DVR_FindFile_V40拿文件列表带文件名回放。4.2 历史流推流播放控制接口与转推时的音画同步按时间回放的代码示意NET_DVR_PLAYBACK_CONDITION_V40 cond new NET_DVR_PLAYBACK_CONDITION_V40(); cond.dwSize cond.size(); cond.struStartTime.setTime(2024-04-01 10:00:00); cond.struStopTime.setTime(2024-04-01 11:00:00); int playHandle HCNetSDK.INSTANCE.NET_DVR_PlayBackByTime_V40( userId, cond, null, null); if (playHandle 0) { throw new RuntimeException(启动历史流失败错误码 HCNetSDK.INSTANCE.NET_DVR_GetLastError()); }拿到playHandle后可以把回放流当作一路源交给 ffmpeg 拉取或转推。如果走 RTSP 地址方案SDK 只用来确认通道是否有录像推流命令不变。播放控制是历史流推流最容易忽略的部分暂停时不能只停止喂数据否则客户端会一直显示缓冲中正确做法是 SDK 的NET_DVR_PausePlay(playHandle)和NET_DVR_ResumePlay(playHandle)配对调用。倍速播放建议限制在 4 倍以内超过 8 倍很多设备输出的 GOP 结构异常copy 转推后画面会突然花一下再恢复。音画同步方面ffmpeg 推历史流不要加-re。加了-re会按照文件时间戳匀速读流等于人为降低倍速不加-re从 RTSP 拉历史流时源端会按录像真实时间输出转推端保持原时间戳就好。4.3 抓图与录像下载两个独立的“证据”功能怎么接抓图是业务方最爱要的功能报警抓图、门禁抓图、巡检留证。SDK 的NET_DVR_CaptureJPEGPicture是最直接的接口传用户 ID、通道号、图片参数和保存路径就能拿到 JPEG。NET_DVR_JPEGPARA jpeg new NET_DVR_JPEGPARA(); jpeg.wPicSize 0xff; // 0xff 表示用设备默认分辨率 jpeg.wPicQuality 1; // 质量档位一般 0-2 boolean ok HCNetSDK.INSTANCE.NET_DVR_CaptureJPEGPicture( userId, channel, jpeg, /data/snapshot/chan_ channel .jpg);wPicSize通常填0xff让设备按当前分辨率抓wPicQuality建议填 1既能控制文件大小又不会太糊。夜间抓图是重灾区红外还没启动或设备还在自动曝光时抓出来就是黑图。我的做法是抓图前先读设备时间接口返回后检查文件大小小于 5KB 就当失败处理过 3 秒重试一次。录像下载通常是配合历史流回放句柄做的先把历史流回放起来再用NET_DVR_SaveRealData(playHandle, fileName)把数据写到本地。保存下来的是 PS 流后缀一般是.ps不是 MP4。要么前端支持 PS 播放要么用 ffmpeg 转一下封装ffmpeg -i record.ps -c:v copy -c:a copy record.mp4这一步必须等回放结束再执行。最常见的错误是回放还没停止就去打开.ps文件文件头不完整转出来只有音频或干脆不能播放。进度可以用NET_DVR_GetDownloadPos轮询存到数据库而不是让前端干等。5. 海康SDK二次开发避坑指南插件提示、通道命名、SRS和抓图权限这一章集中写我踩过的坑都是生产环境里能复现的问题按现象、原因、解决三条来记。5.1 页面提示“请点击此处下载插件”但装不上浏览器插件方案已经过时现象在海康官方 Web 接入页打开实时预览页面反复提示“请点击此处下载插件”下载安装包后安装又提示“安装时请关闭浏览器”不管 win10 还是 win11 的浏览器都很难装成功。原因海康老方案依赖 ActiveX 或 NPAPI 插件Chrome 早就禁用了 NPAPIWindows 下 64 位浏览器也不兼容 32 位插件。这条技术路线已经走到头了。解决不要把时间花在兼容插件上。Java 服务端通过 SDK 拿到流再转推成 RTMP/HLS前端用 flv.js 或 HLS 播放。页面只播流不装插件。这个改动看着大实际就是换成第 3 章那套推流链路比适配一堆浏览器版本省太多事。5.2 NVR通道号命名规则看不懂RTSP地址拼错返回401/404现象同一台 NVRStreaming/Channels/101能出画面102黑屏换成201又提示 404。原因RTSP 路径里的三位数不是简单自增。第一位是通道号第二位固定是 0第三位是码流类型而且 NVR 的 IP 通道编号和设备面板上的物理通道不一定一致。海康录像机命名规则在不同固件上还有差异有的型号会省略中间的 0。解决不要靠猜。登录 SDK 后从设备信息里读出通道列表逐个通道测试或者打开设备网页端的取流地址页面照抄它自己生成的 URL。排查时先用 VLC 试地址VLC 能播说明地址没问题再查代码。返回 401 时第一反应别改密码先用 ffprobe 看响应是 401 还是 404401 很可能是路径或权限问题404 才是地址不存在。5.3 SRS推流不稳定先怀疑源端码流再怀疑服务器现象ffmpeg 把海康实时流推到 SRS播放端看一会儿就卡顿延迟越来越大换个播放器又提示不支持 x264。原因多数时候不是 SRS 不稳定而是源端 RTSP 用了 UDP 传输导致丢包或者 GOP 太长。OBS 推流时显示不支持 x264 的类似问题本质也是编码参数和封装格式不匹配到了服务器端就表现为花屏和卡顿。解决ffmpeg 拉流强制加-rtsp_transport tcp码率不要贪高1080p 监控 4Mbps 足够如果源编码是 H.265RTMP/FLV 兼容性差用-c:v libx264 -preset veryfast -g 25转码。改完之后再观察 SRS 的连接数、带宽和播放器缓冲水位问题通常就消失了。5.4 历史流回放快进后花屏ffmpeg退出码频繁139现象历史流普通速度播放正常倍速一开就花屏甚至 ffmpeg 直接段错误退出退出码 139。原因部分固件在倍速回放时会跳过一些参考帧或者输出流的时间戳跳动直接 copy 封装成 FLV 后播放端解码错乱。解决回放推流不要无脑 copy至少给视频加-c:v libx264 -profile:v main转一道倍速控制在 4 倍以内另外把-max_delay设为500000微秒给音视频同步留一点余量。如果仍然花屏可以在 SDK 层不用倍速改成按时间跳转重新拉流用“播放 2 倍时间范围的长录像”来模拟倍速播放端体验更稳。5.5 抓图黑图或返回NET_DVR_NOENOUGHPRI权限和时机各占一半现象抓图接口返回NET_DVR_NOENOUGHPRI或者返回成功但存了一张全黑 JPEG。原因登录用户没有勾选远程抓图权限或者摄像头在夜间还没完成红外切换自动曝光也没稳定立刻抓就是黑图。海康摄像头夜晚不灵敏很多时候不是设备坏了是时序问题。解决给 SDK 用户分配操作权限抓图前先查设备时间夜里抓图时重试间隔放到 3 秒以上检查文件大小异常就重抓。抓图接口本身不占用实时流句柄但部分固件要求有预览会话抓图超时时先确认主码流通道是否已被其他会话占满。6. 推流不是启动完就结束用ffprobe做存活检测与断流自动拉起6.1 检测RTMP流是否“真活”ffprobe拉流视角进程在不代表流在。ffmpeg 可能卡在底层 socket 上也可能是 NVR 端会话断开但进程没退出。我一般用 ffprobe 去拉一次 RTMP 流判断返回结果里是否有视频 codec。#!/bin/bash streamrtmp://192.168.1.10:1935/live/camera_101 if timeout 8 ffprobe -v error -print_format json -show_streams $stream \ | grep -q codec_type: video; then echo alive exit 0 else echo dead exit 1 fitimeout 8是防止 ffprobe 在流服务器不响应时挂死只检测进程 PID 不靠谱因为僵尸进程往往还在。注意 JSON 输出里字段空格在不同 ffprobe 版本会有差异用grep -q codec_type: *video更稳。检测脚本放进定时任务每 30 秒跑一次。连续两次失败就把旧进程 kill 掉清掉 RTMP 残留状态再重新拉起。重试要带熔断连续 5 次失败后发告警不要无限循环否则设备离线时日志会被刷爆。6.2 端到端延迟验证别只看ffmpeg日志ffmpeg 日志只能说明它读到了流不能说明客户端看到的画面延迟多大。一个我自己常用的土办法在摄像头画面里放一个有秒针的时钟推流端拉流抓一帧播放端也抓一帧对比两张图上的时间差。差在 1 秒内是正常超过 3 秒就检查播放器缓存和 SRS 的gop_cache配置。抓帧命令很简单ffmpeg -rtsp_transport tcp -i $stream -frames:v 1 /tmp/frame.jpg对严格低延迟的业务不建议用海康默认码流参数把 I 帧间隔调到 25、码率固定、关闭 SRS 的gop_cache延迟能降很多。这套存活检测和验证逻辑我现在每个通道任务都会带上。推流服务上线后宁可多写脚本自己测也不要等用户反馈“画面不动了”。这个习惯帮我避开了很多次半夜被叫醒希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑