☰
Java调用海康SDK实战:JNA加载与监控功能集成
2026/10/1 19:08:05 网站建设 项目流程

简介:面向Java开发者的海康威视网络摄像机与NVR二次开发资源包,适用于安防监控系统集成、视频平台开发等场景,解决实时流/历史流推流、抓图、录像下载及云台控制等二次开发需求。资源围绕海康SDK封装了Java调用示例,涵盖设备初始化、登录、通道管理、码流获取等关键接口,便于理解对接流程。包内共255个文件,约39.92MB,以49个Java源码为主体,包含131个XML配置,25个DLL与23个SO动态库覆盖Windows/Linux平台,另附JAR依赖、启动脚本及SQL文件。目前已有203人学习下载,该资源适合需要快速搭建监控应用或维护现有项目的Java工程师。借助它可快速实现网络摄像机/NVR的实时与历史流推流,掌握抓图、录像下载和云台控制的编码思路,同时获得跨平台库配置与工程部署经验,显著缩短SDK联调周期。

1. Java调海康SDK:为什么绕不开native库加载这道坎

Java调用海康威视网络摄像机和NVR,最直接的路就是官方SDK的JNA封装。这个资源包里躺着PlayCtrl.dll、HCNetSDK.dll、libcrypto.so.1.0.0这一堆native库,懂行的人一眼就看出是标准的Win32+Linux双平台SDK。它解决的问题很具体:让你在Java进程里完成设备登录、实时流预览、历史流回放、抓图、录像下载和云台控制,而不是只能打开海康的iVMS客户端。适合正在做安防平台对接、需要把监控能力嵌进Java后端服务的人。我拆过这个包,把常用调用路径和踩过的坑都整理出来,照着走能少折腾一个礼拜。

2. 海康SDK资源包解析:从DLL/so文件到JNA接口映射

2.1 SDK包里每个文件的实际职责

海康SDK的Java资源目录里文件不少,但真正要关心的是下面这几种。先看一张对照表,后面写代码时能少绕弯路:

文件平台职责
HCNetSDK.dllWindows网络SDK核心,设备登录、取流、控制全在这
libhcnetsdk.soLinux同上,Linux下的核心库
PlayCtrl.dllWindows播放解码库,预览画面渲染用
SuperRender.dllWindows高性能渲染库,配合PlayCtrl使用
AudioIntercom.dllWindows语音对讲功能
HCGeneralCfgMgr.dllWindows通用配置管理
HCCore.dllWindows核心基础库
libcrypto.so.1.0.0LinuxOpenSSL密码库,SDK依赖
libopenal.so.1LinuxOpenAL音频库,音频解码用
libeay32.dllWindows老版OpenSSL库,HCNetSDK在Windows下的依赖
start.batWindows启动脚本,设置环境变量用

提示:Windows下如果把PlayCtrl.dll漏了,登录设备没问题,但NET_DVR_RealPlay一旦要显示画面就直接崩。Linux下常见坑是libcrypto.so.1.0.0和系统自带OpenSSL版本冲突,这个在避坑章专门说。

这些native库不能在Java里直接用,必须通过JNA或JNI桥接。海康官方把HCNetSDK的接口声明封装成了一个Java接口文件,理论上你只要把它拷到项目里就能调。实际打包部署时,dll和so文件要按平台放到对应目录,classpath里还得能扫到。

2.2 JNA接口定义与加载方式

先说结论:优先用JNA,别碰JNI。JNI要自己写C代码再javac生成头文件,海康SDK几百个接口,用JNI一个月都搞不完。JNA把这一步省了,只靠Java接口声明就能加载动态库。

在pom.xml里引入JNA依赖:

<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.13.0</version> </dependency>

然后定义接口:

import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Pointer; public interface HCNetSDK extends Library { // 全局唯一实例,JNA内部自动加载HCNetSDK.dll或libhcnetsdk.so HCNetSDK INSTANCE = (HCNetSDK) Native.load("HCNetSDK", HCNetSDK.class); // 初始化SDK,返回true表示成功 boolean NET_DVR_Init(); // 设置连接和重连超时时间 boolean NET_DVR_SetConnectTime(int dwWaitTime, int dwTryTimes); // 登录设备,成功返回true,lUserID通过结构体传出 boolean NET_DVR_Login_V40(HCNetSDK.NET_DVR_USER_LOGIN_INFO pLoginInfo, HCNetSDK.NET_DVR_DEVICEINFO_V40 lpDeviceInfo); // 退出登录 boolean NET_DVR_Logout(int lUserID); // 释放SDK资源 boolean NET_DVR_Cleanup(); }

代码逻辑说明:JNA的Native.load("HCNetSDK")按操作系统推断——Windows下找HCNetSDK.dll,Linux下找libhcnetsdk.so。INSTANCE是单例,所有接口调用都走它,不要重复加载,否则两个实例的native句柄不共享。接口方法名必须和C库导出函数名一致,大小写敏感。

参数说明:NET_DVR_SetConnectTime里dwWaitTime是每次连接尝试的超时毫秒数,dwTryTimes是重试次数。设备不在网段内时默认可能要等20秒才报错,把dwWaitTime调到2000能快速失败。这个参数在批量巡检设备时特别重要——一台离线设备卡20秒,100台就是半小时。

2.3 初始化、登录与退出标准流程

一段完整的登录代码大致是这样:

public class HikCameraConnector { private HCNetSDK sdk = HCNetSDK.INSTANCE; private int userID = -1; public boolean connect(String ip, int port, String username, String password) { // 1. 初始化SDK if (!sdk.NET_DVR_Init()) { System.err.println("SDK初始化失败"); return false; } // 2. 配置连接参数:连接超时2秒,重试1次 sdk.NET_DVR_SetConnectTime(2000, 1); // 3. 组装登录信息结构体 HCNetSDK.NET_DVR_USER_LOGIN_INFO loginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO(); loginInfo.wPort = port; loginInfo.sDeviceAddress = ip.getBytes(); loginInfo.sUserName = username.getBytes(); loginInfo.sPassword = password.getBytes(); loginInfo.bUseTransport = 0; // 0=TCP, 1=UDP HCNetSDK.NET_DVR_DEVICEINFO_V40 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40(); // 4. 执行登录 if (!sdk.NET_DVR_Login_V40(loginInfo, deviceInfo)) { int err = sdk.NET_DVR_GetLastError(); System.err.println("登录失败,错误码: " + err); return false; } userID = deviceInfo.lUserID; // 实际lUserID从结构体取 return true; } public void disconnect() { if (userID >= 0) { sdk.NET_DVR_Logout(userID); userID = -1; } sdk.NET_DVR_Cleanup(); } }

代码逻辑说明:NET_DVR_Login_V40的第一个参数是登录信息结构体,第二个参数回传设备能力信息——包括设备序列号、通道数量和报警输入输出个数,后续预览调用要用到通道数。lUserID是后续所有操作的凭证,丢了就要重新登录。

参数说明:sDeviceAddress是byte数组,Java里String转byte[]要注意编码。海康默认按GBK处理字符串,IP地址纯ASCII用getBytes()没问题,但设备名含中文时建议显式转GBK:sDeviceAddress = "中文设备名".getBytes("GBK")。bUseTransport建议设0走TCP,UDP虽然延迟低但丢包重传麻烦。

初始化顺序有个容易忽略的点:NET_DVR_Init和NET_DVR_Cleanup必须成对出现,且整个JVM进程内最好只调用一次。如果多个业务模块都要连海康设备,不要让每个模块单独Init/Cleanup,否则后创建的引用会悬空。

2.4 Linux部署与Windows部署的差异

Windows开发、Linux部署是Java后端最常见的路径。Windows上把dll文件放在jdk的bin目录或项目根目录都能被JNA扫到,IDE里跑通常没问题。Linux上依赖关系更复杂,除了libhcnetsdk.so自身,它还依赖libcrypto.so.1.0.0等openssl老版本库。

我一般的做法是:把SDK包里的所有so文件拷贝到项目的libs目录,启动脚本里用LD_LIBRARY_PATH指过去。注意这个环境变量只对当前进程生效,不会污染系统全局。

#!/bin/bash export LD_LIBRARY_PATH=$(pwd)/libs:$LD_LIBRARY_PATH java -Xmx512m -jar monitor-service.jar

这段脚本的逻辑是先把libs目录加入动态库搜索路径,再启动Java进程。如果漏掉export,SDK初始化时直接报UnsatisfiedLinkError,错误信息会提示找不到某so文件。还有一点,linux下so的依赖是不能递归自动解决的,libhcnetsdk.so依赖libcrypto,但libcrypto又可能依赖其他系统库,缺一不可。

3. 实时流与历史流推流:接口调用序列与参数设置

3.1 实时流预览:从设备取流到回调函数

登录之后,实时流走NET_DVR_RealPlay。这个接口把视频流从设备拉下来,通过回调函数实时吐给你。注意它的第三个参数是回调函数指针,Java里用JNA的Callback接口承接。

public class HikRealStream { private HCNetSDK sdk = HCNetSDK.INSTANCE; // 定义数据回调接口,这是JNA的StdCallCallback子接口 public interface RealDataCallback extends StdCallCallback { void invoke(int lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, Pointer pUser); } private int realHandle = -1; private RealDataCallback callback; public void startPreview(int userID, int channel) { HCNetSDK.NET_DVR_CLIENTINFO clientInfo = new HCNetSDK.NET_DVR_CLIENTINFO(); clientInfo.lChannel = channel; // 通道号从0开始 clientInfo.lLinkMode = 0; // 0=TCP取流, 1=UDP, 2=组播 clientInfo.hPlayWnd = null; // null表示不渲染画面,纯取流 callback = (handle, dataType, buf, bufSize, user) -> { // 数据回调:buf里是PS封装的视频流 // 这里只做拷贝和入队,别做耗时操作 byte[] data = buf.getByteArray(0, bufSize); streamQueue.offer(data); }; realHandle = sdk.NET_DVR_RealPlay(userID, clientInfo, callback, null); if (realHandle == -1) { int err = sdk.NET_DVR_GetLastError(); throw new RuntimeException("RealPlay失败,错误码=" + err); } } }

代码逻辑说明:NET_DVR_RealPlay的返回值是预览句柄,停止预览时用NET_DVR_StopRealPlay(realHandle)关闭。回调函数里dwDataType常见值是0(视频流)和1(音频流),pBuffer指向的数据是PS流格式,不能直接当H.264裸流用,后面进阶章会讲怎么解封装。streamQueue建议用LinkedBlockingQueue,容量限到4096,防止内存被堆满。

参数说明:hPlayWnd在纯后端推流场景传null,避免弹出窗口。lLinkMode选0(TCP)最稳;如果设备在弱网环境想降低延迟,可以考虑组播,但交换机和摄像头都要配置组播协议,工程复杂度高。channel从0开始,对应设备Web配置里的通道1;NVR设备通道号从0到最大通道数减1,超出直接返回错误。

3.2 历史流回放:按时间检索与播放

历史流和实时流的区别在于源头——实时流从摄像头传感器取,历史流从NVR硬盘取。回放先要按时间段检索录像文件,再下发播放请求。

public void playBackByTime(int userID, int channel, String startTime, String stopTime) { HCNetSDK.NET_DVR_PLAYBACKCOND cond = new HCNetSDK.NET_DVR_PLAYBACKCOND(); cond.lChannel = channel; cond.dwPlayMode = 0; // 0=按时间回放,1=按文件名回放 // 时间格式:yyyy-MM-dd HH:mm:ss cond.struStartTime = parseTime(startTime); cond.struStopTime = parseTime(stopTime); int playHandle = sdk.NET_DVR_PlayBackByTime(userID, cond); if (playHandle == -1) { int err = sdk.NET_DVR_GetLastError(); if (err == 17) { System.out.println("该时间段无录像"); } else { System.out.println("回放失败,错误码=" + err); } return; } // 之后同样挂回调收数据,数据格式仍是PS流 }

代码逻辑说明:NET_DVR_PlayBackByTime返回的是回放句柄,数据通过回调函数逐帧下发。按时间回放更通用,因为前端只需要给时间范围,不需要知道文件名。如果业务场景是下载某个具体录像文件,用NET_DVR_PlayBackByName更直接。

参数说明:错误码17(NET_DVR_NOSPURCHASED)表示该时间段没有录像,这是回放场景最常遇到的错误码,不是程序bug,是设备端确实没录上。另外回放接口对时间跨度有限制,单次查询不要超过24小时,跨天检索先拆成两段。

3.3 录像下载:把历史流落盘

录像下载本质和回放一样,只是数据不再走回调给人看,而是直接由SDK写入文件:

public void downloadVideo(int userID, String remotePath, String localPath) { // 文件句柄 int handle = sdk.NET_DVR_GetFileByName(userID, remotePath, localPath); if (handle == -1) { throw new RuntimeException("获取文件失败,错误码=" + sdk.NET_DVR_GetLastError()); } // 开始下载 if (!sdk.NET_DVR_PlayBackControl(handle, 0, 0, null)) { sdk.NET_DVR_StopPlayBack(handle); throw new RuntimeException("开始下载失败"); } // 轮询检查下载进度 while (true) { HCNetSDK.NET_DVR_PLAYBACK_STATE state = new HCNetSDK.NET_DVR_PLAYBACK_STATE(); sdk.NET_DVR_GetPlayBackStatus(handle, state); if (state.dwSize > 0 && state.dwProgress == 100) { break; } Thread.sleep(500); } sdk.NET_DVR_StopPlayBack(handle); System.out.println("下载完成: " + localPath); }

代码逻辑说明:NET_DVR_GetFileByName用于下载指定录像文件,remotePath的格式形如/xxx/xxx/20240101120000.000。这个路径需要先用NET_DVR_FindFile逐条遍历才能拿到,不能自己拼。下载进度通过NET_DVR_GetPlayBackStatus轮询,dwProgress到100表示写盘完成。

参数说明:PlayBackControl的第二个参数0表示开始播放/下载。下载期间不要频繁操作设备,NVR的磁盘IO能力有限,并行下载多个文件会明显变慢,建议一个设备同时最多下两个文件,超过会排队等待。

3.4 多通道并发取流

海康NVR一个设备可能带16路甚至64路摄像头,做并发拉流时要控制线程数。常见做法是每通道一个取流线程,但底层句柄数和socket连接数不能无限涨:

ExecutorService streamPool = Executors.newFixedThreadPool(runtime.getRuntime().availableProcessors() * 2); for (int ch = 0; ch < channelCount; ch++) { int finalCh = ch; streamPool.submit(() -> { try { HikRealStream stream = new HikRealStream(); stream.startPreview(userID, finalCh); // 每个通道独立消费回调数据 stream.consume(); } catch (Exception e) { log.error("通道{}取流失败", finalCh, e); } }); }

代码逻辑说明:线程池大小按CPU核数2倍起步,而不是等于通道数,因为取流回调已经在SDK的工作线程里执行,Java侧线程只是消费队列里的数据。通道数多于线程池容量时,建议使用阻塞队列让多余通道等待,避免一次几十个线程同时向NVR发起RTSP取流。

参数说明:NVR设备能承受的并发取流通道数取决于设备型号,入门级NVR同时8路没问题,高端型号可以到32路。超过上限时设备会拒绝连接或直接丢流,日志里能看到错误码35(网络异常)。

4. 抓图、录像下载与云台控制:三个高频功能落地

4.1 抓图:JPEG直出与定时抓图

抓图分两种:手动抓图和定时抓图。手动抓图用NET_DVR_CaptureJPEG,直接把一帧画面编码成JPEG文件;定时抓图就是在你后端起个调度器,按cron表达式循环调用。

public boolean captureJpeg(int userID, int channel, String savePath) { HCNetSDK.NET_DVR_JPEGPARA jpegPara = new HCNetSDK.NET_DVR_JPEGPARA(); jpegPara.wPicSize = 0; // 0=按原分辨率 jpegPara.wPicQuality = 2; // 质量系数 0-4,2是中等 boolean ok = sdk.NET_DVR_CaptureJPEG(userID, channel, jpegPara, savePath); if (!ok) { System.err.println("抓图失败: " + sdk.NET_DVR_GetLastError()); } return ok; }

代码逻辑说明:NET_DVR_CaptureJPEG把JPEG图片直接写到本地路径或网络共享目录,Java场景下通常传一个与设备可达的共享目录。海康设备对图片格式有白名单,扩展名必须是.jpg或.jpeg。

参数说明:wPicQuality的0~4对应压缩质量,0最好但文件最大,2是现场常用的均衡值。IPC的抓图延迟一般200~500毫秒,连续抓图时建议间隔1秒以上,否则部分老型号会返回23号错误码(资源不足)。

4.2 定时抓图的调度设计

工程上更常用的是定时抓图,比如每分钟抓一张门禁截图。用Java自带的ScheduledExecutorService就够了,不需要引Quartz:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); // 每分钟执行一次抓图任务 scheduler.scheduleAtFixedRate(() -> { try { String filePath = "/data/snap/" + System.currentTimeMillis() + ".jpg"; boolean ok = captureJpeg(userID, 0, filePath); if (!ok) { log.warn("定时抓图失败,错误码={}", sdk.NET_DVR_GetLastError()); } } catch (Exception e) { log.error("定时抓图异常", e); } }, 0, 60, TimeUnit.SECONDS);

代码逻辑说明:scheduleAtFixedRate按固定频率执行,任务执行时间超过间隔会等待当前任务结束后立即补一次,不会跳过。抓图失败要记录日志但不能抛异常,否则调度线程会被Kill。这个设计在处理几十台设备的定时抓图时比Cron表达式直观得多。

4.3 云台控制PTZ:方向、速度和停止指令

云台控制接口NET_DVR_PTZControl,常见操作是上下左右和缩放。注意这是个持续动作:按下时调用PTZControl(handle, cmd, 1),松开时调用PTZControl(handle, cmd, 0),中间漏了一次停止指令,云台会一直转到机械限位。

public void moveUp(int userID, int channel, int speed) { // 速度范围1-7,超范围收敛 if (speed < 1) speed = 1; if (speed > 7) speed = 7; // 开始转动,11是云台上移指令码 sdk.NET_DVR_PTZControl(userID, 11, 1); try { Thread.sleep(500); // 转500毫秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 必须发停止,否则云台停不下来 sdk.NET_DVR_PTZControl(userID, 11, 0); }

代码逻辑说明:云台控制码1=上、2=下、3=左、4=右,11=上。启动和停止必须成对调用,启动后业务上即使报错也要在finally里补发停止指令。实际项目中我封装了一个safeStop方法,把所有的PTZ停止调用统一收口。

参数说明:云台速度由NET_DVR_PTZControlWithSpeed_Other设置,范围1~7。速度到达目标位置的精度和云台电机有关,步进电机建议速度≤3,否则惯性过冲会跑到目标位置外面。

4.4 设备在线巡检与状态探测

做安防平台必然要周期性地探测设备是否在线,不能光靠TCP ping。海康SDK有专门的设备状态探测接口:

public boolean isDeviceOnline(int userID, String deviceIP) { // 获取设备工作状态 HCNetSDK.NET_DVR_WORKSTATE workState = new HCNetSDK.NET_DVR_WORKSTATE(); boolean ok = sdk.NET_DVR_GetDVRWorkState(userID, workState); if (!ok) return false; // workState里有设备各通道的录像状态和连接状态 return workState.byChannel > 0; }

代码逻辑说明:NET_DVR_GetDVRWorkState返回设备整体工作状态,包括通道个数、录像状态等。这个接口适合做5分钟级别的巡检,如果设备掉线早发现早处理。比单纯ping IP可靠,因为设备可能网络通但SDK服务异常。

参数说明:巡检频率不要低于1分钟,海康设备的前端登录会话有限,频繁探测会把设备侧的session占满。推荐每个设备5分钟一次,配合告警通知足够。

5. 避坑专篇:native库加载失败、回调线程与句柄泄漏

5.1 现象:Linux下加载libcrypto.so.1.0.0失败

在CentOS 7上跑java -jar,启动时报UnsatisfiedLinkError,提示找不到libcrypto.so.1.0.0。原因:海康SDK依赖OpenSSL 1.0.0的特定版本,而系统自带的OpenSSL是1.0.2k或1.1.1,so文件名对不上。解决:把SDK包里自带的libcrypto.so.1.0.0拷贝到jdk目录的lib/amd64下,或者设置LD_LIBRARY_PATH。我习惯把SDK的linux_so目录加到启动脚本里,用export LD_LIBRARY_PATH=./libs:$LD_LIBRARY_PATH。还有个衍生问题:如果服务器同时跑着nginx,它可能引用系统OpenSSL,改LD_LIBRARY_PATH会影响进程内其他库的加载顺序,建议用rpath或把so直接放到JRE的lib路径下。

5.2 现象:回调函数里做耗时业务导致崩溃或丢帧

回调是SDK的工作线程在调用,如果在这个线程里做数据库写操作、sleep、网络请求,轻则丢帧,重则JVM直接崩掉。原因:海康回调线程栈空间有限,Java层长时间占用会阻塞SDK的取流缓冲区,缓冲区满了就丢帧。解决:回调函数里只做一件事——把数据复制成字节数组丢进队列,业务消费者线程从队列取数据处理。常见做法是用LinkedBlockingQueue,队列容量限到4096,超出后丢弃最旧帧,保持实时性。

5.3 现象:句柄泄漏,内存和连接数持续上涨

连续运行三天后,设备侧显示连接数爆满,进程内存也不断涨。原因:每次登录获得userID,退出时只调了NET_DVR_Logout,漏了NET_DVR_Cleanup;或者预览句柄没停止直接登出。解决:写一个统一释放的工具方法,登录、预览、回放、下载四个句柄都归它管,在finally里执行完整释放链。我踩过最狠的一次是回调线程野指针,排查到最后发现是回调里用了某个已经Logout的userID,之后只要回调数据一来就崩。从那以后我每次登录和退出都强制走一遍统一的token管理器,所有句柄的分配和释放都在同一个类里进出。

5.4 现象:win10浏览器加载不了海康web插件

做web对接时,用户用Chrome打开页面提示需要安装插件,装完还是白屏。原因:海康的传统web控件基于ActiveX/NPAPI,Chrome从45版本起就禁用了NPAPI,新版直接不支持。解决:后端集成SDK做流媒体转发,前端用WebSocket收HLS或WebRTC流;如果必须用官方web组件,只能在IE模式或老Edge兼容模式下跑,但那是给演示环境用的,生产还是得走后端拉流这条路。这也是为什么需要有Java SDK二次开发能力——在服务端把实时流和历史流转成前端能直接播放的协议。

5.5 现象:抓图偶尔黑屏或花屏

抓图任务跑几天后,偶发黑屏或花屏。原因:多半是RTSP取流丢关键帧,JPEG编码时I帧不完整导致花屏。解决:抓图失败时重试两次,间隔300毫秒;如果还不行,重新登录设备拿新会话再抓一次。另外检查网络质量,海康设备要求TCP连接丢包率低于1%,用有线网络部署最稳。

6. 进阶点睛:把回调里的PS流转成H.264裸流并推RTMP

6.1 从回调buf中切出完整帧

回调数据是PS流,里面包含视频和音频的PES包。FFmpeg有个解复用器能直接处理PS流,但很多Java工程不想额外拉C库,那就自己切。PS流的视频PES包开头是00 00 01 E0,找到这个header后,跳过PES header,里面的就是H.264的NAL单元。由于回调是按缓冲区切片的,一个包可能跨多个PES,所以要做粘包处理。

public void processPSToH264(byte[] buffer, int size) { for (int i = 0; i + 6 < size; i++) { if ((buffer[i] & 0xFF) == 0x00 && (buffer[i+1] & 0xFF) == 0x00 && (buffer[i+2] & 0xFF) == 0x01 && (buffer[i+3] & 0xFF) == 0xE0) { int pesLen = ((buffer[i+4] & 0xFF) << 8) | (buffer[i+5] & 0xFF); int payloadStart = i + 6 + 3; // 跳过PES固定头 writeToFrameBuffer(buffer, payloadStart, i + 6 + pesLen - payloadStart); } } }

这段代码的逻辑是扫描缓冲区找PES同步字0x000001E0,找到后用PES头声明长度计算载荷边界。注意不同编码器产出的PES头长度可能不同,这里按简化场景处理。切出来的NALU里I帧前面一定带着SPS/PPS,推流时这两个参数最容易被忽略,丢了会导致播放端黑屏。

6.2 用FFmpeg推流到RTMP

拿到H.264裸流后推到RTMP服务器,我一般用FFmpeg命令行模式接管,Java侧通过ProcessBuilder拉起子进程,把流数据写进它的stdin:

ffmpeg -f h264 -i pipe:0 -c:v copy -f flv rtmp://192.168.1.100/live/camera01

Java侧用ProcessBuilder启动这个进程,拿到stdin输出流,把切好的H.264帧写进去。这样省去在Java里再集成一份libavcodec的麻烦。如果服务器有NVIDIA显卡,把-c:v copy换成-c:v h264_nvenc就能用GPU转码,推H.265同样封装。

这个方法做下来,海康设备到流媒体服务器的全链路就通了。从那以后我每次做取流、回放、抓图三件套,都先把句柄生命周期管理写在最前面,再写业务代码,这套流程帮我少加了很多次夜班。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询