(修改前言:最近公司需求又对接海康的人脸设备,也就刚刚好有时间重新整理写的这篇博客,看评论也有很多人说有问题 demo无法跑通 小编在这呢 也重新测一遍 也会将一些细节更详细的列出来吗 同时呢 除了之前写的这套方案,小编在这也会再提供一个方案)

写在前面:

最近也遇到了调用海康多个摄像头实现同时预览的需求,不`过官方demo里并没详细的案例,上网查了下资料,也找不到对应的解决方案 ,电话咨询海康技术,没接过,信息没回过。这里就不对海康技术支持多作评价了,废话不多说。上方案!

小编首先整理了文档和获取流程(如图)

然后小编根据这个思路将SDK的Demo流程整个看了一遍,终于发现了问题的所在,接下来直接上代码(个人是根据海康提供的demo进行简单的修改,将部分公共代码给抽了出来,另外小编只用到了预览录制和停止录制功能,解码和其他功能小编没用到,所以小编在这里都干掉了。尽量能简洁易懂就简介易懂,大佬勿喷!仅供参考和提供思路!!!!!!)

1 首先去官网下载海康的demo和文档,地址不知道?没关系 我给你啦!

https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10

选择立即下载

2 选择java开发示例,然后选择预览回放和下载

官网的demo示例就在这啦 不过 要注意官方的提示文件 根据上面的注意事项 导入文件到你的项目中

(然后就可以开始进行Demo调试啦)

具体流程如下:

1 初始化设备 并定义一个list集合存储用户句柄

public static List lUserIDList = new ArrayList();//用户句柄集合 //初始化private static void init() {//SDK初始化,一个程序只需要调用一次boolean initSuc = hCNetSDK.NET_DVR_Init();if (initSuc != true) {System.out.println("初始化失败");}//异常消息回调if (fExceptionCallBack == null) {fExceptionCallBack = new FExceptionCallBack_Imp();}Pointer pUser = null;if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, pUser)) {return;}System.out.println("设置异常消息回调成功");//启动SDK写日志hCNetSDK.NET_DVR_SetLogToFile(3, "..\\sdkLog\\", false);}

2 登陆设备(只是演示和提供思路 具体实现请各位大佬根据实际业务)

登陆一台设备就会返回一个用户句柄也可称登陆句柄,后续调用预览是根据用户句柄来区分和管理的。

唯一注意的是,设备登陆后的返回值需要保存!登陆完后创建保存回调函数的音频数据文件

(注意:你的设备要和你自己的网络在同一个网段!!!!!!!查看电脑的网段 win+R 输入cmd 你的电脑是192.16.0段的 把摄像头的ip地址和网关也改成同段的 不然会报错误码7或错误码8等)

//登陆private static void login(){//存储登陆设备集合List list = new ArrayList();Device device = new Device();device.setIp("192.168.1.16"); //改成自己设备的ip 用户名和密码device.setUserName("admin");device.setPassWord("Admin123");Device devic1 = new Device();device.setIp("192.168.1.17");device.setUserName("admin");device.setPassWord("Admin123");list.add(device);list.add(devic1);//登录设备,每一台设备分别登录; 登录句柄是唯一的,可以区分设备HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo;//设备登录信息HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo;//设备信息for (Device d : list) {m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息String m_sDeviceIP = d.getIp();//设备ip地址m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());String m_sUsername = d.getUserName();//设备用户名m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());String m_sPassword = d.getPassWord();//设备密码m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length());m_strLoginInfo.wPort = 8000; //SDK端口m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是m_strLoginInfo.write();lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);//将登陆返回的用户句柄保存!(这里很重要 是原先官网没有的,这里保存句柄是为了预览使用)lUserIDList.add(lUserID);if (lUserID==-1) {System.out.println("登录失败,错误码为" + hCNetSDK.NET_DVR_GetLastError());} else {System.out.println("设备登录成功! " + "设备序列号:" + new String(m_strDeviceInfo.struDeviceV30.sSerialNumber).trim());m_strDeviceInfo.read();}//保存回调函数的音频数据VideoDemo.setFile(lUserID);}}

3 创建保存回调函数的音频数据文件 同时创建一个map集合 保存对应的文件输出流 键为登陆后返回的用户句柄

//定义流的map集合 键为用户句柄(也就是你登陆返回的句柄) static Map outputStreamMap = new HashMap(); public static void setFile(int userId) {file = new File("/Download/" + new Date().getTime() + "(" + userId + ")" + ".mp4");//保存回调函数的音频数据if (!file.exists()) {try {file.createNewFile();} catch (Exception e) {e.printStackTrace();}}//FileOutputStream outputStream=new FileOutputStream(file);try {outputStreamMap.put(userId, new FileOutputStream(file));} catch (FileNotFoundException e) {e.printStackTrace();}}

4 线程run中执行预览代码 通过调用NET_DVR_RealPlay_V40实现预览

 @Overridepublic void run() {fRealDataCallBack = null;if (userId == -1) {System.out.println("请先注册");return;}HCNetSDK.NET_DVR_PREVIEWINFO strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();strClientInfo.read();strClientInfo.hPlayWnd =null;//窗口句柄,从回调取流不显示一般设置为空strClientInfo.lChannel = 1;//通道号strClientInfo.dwStreamType = 0; //0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推strClientInfo.dwLinkMode = 0; //连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4- RTP/RTSP,5- RTP/HTTP,6- HRUDP(可靠传输) ,7- RTSP/HTTPS,8- NPQstrClientInfo.bBlocked = 1;strClientInfo.write();if (fRealDataCallBack == null) {fRealDataCallBack = new FRealDataCallBack();}//开启预览lPlay = hCNetSDK.NET_DVR_RealPlay_V40(userId, strClientInfo, fRealDataCallBack, null);if (lPlay == -1) {int iErr = hCNetSDK.NET_DVR_GetLastError();System.out.println("取流失败" + iErr);return;}System.out.println("取流成功");}

5 修改回调函数这里的回调函数参数都是sdk规定好的在回调中取出集合中对应的流进行文件输出

static class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {//预览回调public void invoke(int lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {//System.out.println(lRealHandle + "码流数据回调" + pBuffer + ", 数据类型: " + dwDataType + ", 数据长度:" + dwBufSize + "puser:" + pUser);long offset = 0;ByteBuffer buffers = pBuffer.getPointer().getByteBuffer(offset, dwBufSize);byte[] bytes = new byte[dwBufSize];buffers.rewind();buffers.get(bytes);try {//根据lRealHandle从map中取出对应的流读取数据outputStreamMap.get(lRealHandle).write(bytes);} catch (Exception e) {e.printStackTrace();}}}

6 开启线程

 public static void main(String[] args) throws InterruptedException {if (hCNetSDK == null && playControl == null) {if (!CreateSDKInstance()) {System.out.println("Load SDK fail");return;}if (!CreatePlayInstance()) {System.out.println("Load PlayCtrl fail");return;}}//linux系统建议调用以下接口加载组件库if (osSelect.isLinux()) {HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);//这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限String strPath1 = System.getProperty("user.dir") + "/lib/libcrypto.so.1.1";String strPath2 = System.getProperty("user.dir") + "/lib/libssl.so.1.1";System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());ptrByteArray1.write();hCNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());ptrByteArray2.write();hCNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());String strPathCom = System.getProperty("user.dir") + "/lib";HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());struComPath.write();hCNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());}//初始化init();//登陆login();// 创建线程池对象指定线程数量ExecutorService tp = Executors.newFixedThreadPool(2);VideoDemo video1=new VideoDemo(lUserIDList.get(0), 1);VideoDemo video2=new VideoDemo(lUserIDList.get(1),1) ;tp.submit(video1);tp.submit(video2);}

以上代码生成的视频在你盘符+Download 文件夹里面去找(如图所示)

注意:你现在生成的视频是无法直接查看的 需要用到海康的播放软件解码查看! 啥软件? 我把链接给你啦

https://www.hikvision.com/cn/support/Downloads/Desktop-Application/Hikvision-Player/

插件打开效果如图:

方案一 完整代码:

package com.NetSDKDemo;import Common.osSelect;import com.Device;import com.sun.jna.Native;import com.sun.jna.Pointer;import java.util.ArrayList;import java.util.List;import java.util.Timer;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * @create 2020-12-24-17:55 */public class ClinetDemo {static HCNetSDK hCNetSDK = null;static PlayCtrl playControl = null;static int lUserID = 0;//用户句柄public static List lUserIDList = new ArrayList();//用户句柄static FExceptionCallBack_Imp fExceptionCallBack;static class FExceptionCallBack_Imp implements HCNetSDK.FExceptionCallBack {public void invoke(int dwType, int lUserID, int lHandle, Pointer pUser) {System.out.println("异常事件类型:" + dwType);return;}}public static void main(String[] args) throws InterruptedException {if (hCNetSDK == null && playControl == null) {if (!CreateSDKInstance()) {System.out.println("Load SDK fail");return;}if (!CreatePlayInstance()) {System.out.println("Load PlayCtrl fail");return;}}//linux系统建议调用以下接口加载组件库if (osSelect.isLinux()) {HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);//这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限String strPath1 = System.getProperty("user.dir") + "/lib/libcrypto.so.1.1";String strPath2 = System.getProperty("user.dir") + "/lib/libssl.so.1.1";System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());ptrByteArray1.write();hCNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());ptrByteArray2.write();hCNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());String strPathCom = System.getProperty("user.dir") + "/lib";HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());struComPath.write();hCNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());}//初始化init();//登陆login();// 创建线程池对象指定线程数量ExecutorService tp = Executors.newFixedThreadPool(2);VideoDemo video1=new VideoDemo(lUserIDList.get(0), 1);VideoDemo video2=new VideoDemo(lUserIDList.get(1),1) ;tp.submit(video1);tp.submit(video2);}//登陆private static void login(){//存储登陆设备集合List list = new ArrayList();Device device = new Device();device.setIp("192.168.1.11");device.setPassWord("admin");device.setPassWord("123456");Device device1 = new Device();device1.setIp("192.168.1.12");device1.setPassWord("admin");device1.setPassWord("123456");list.add(device);list.add(device1);//登录设备,每一台设备分别登录; 登录句柄是唯一的,可以区分设备HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo;//设备登录信息HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo;//设备信息for (Device d : list) {m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息String m_sDeviceIP = d.getIp();//设备ip地址m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());String m_sUsername = d.getUserName();//设备用户名m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());String m_sPassword = d.getPassWord();//设备密码m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length());m_strLoginInfo.wPort = 8000; //SDK端口m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是m_strLoginInfo.write();lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);//将登陆返回的用户句柄保存!(这里很重要 是原先官网没有的,这里保存句柄是为了预览使用)lUserIDList.add(lUserID);if (lUserID==-1) {System.out.println("登录失败,错误码为" + hCNetSDK.NET_DVR_GetLastError());} else {System.out.println("设备登录成功! " + "设备序列号:" + new String(m_strDeviceInfo.struDeviceV30.sSerialNumber).trim());m_strDeviceInfo.read();}//保存回调函数的音频数据VideoDemo.setFile(lUserID);}}//初始化private static void init() {//SDK初始化,一个程序只需要调用一次boolean initSuc = hCNetSDK.NET_DVR_Init();if (initSuc != true) {System.out.println("初始化失败");}//异常消息回调if (fExceptionCallBack == null) {fExceptionCallBack = new FExceptionCallBack_Imp();}Pointer pUser = null;if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, pUser)) {return;}System.out.println("设置异常消息回调成功");//启动SDK写日志hCNetSDK.NET_DVR_SetLogToFile(3, "..\\sdkLog\\", false);}/** * 动态库加载 * * @return */private static boolean CreateSDKInstance() {if (hCNetSDK == null) {synchronized (HCNetSDK.class) {String strDllPath = "";try {if (osSelect.isWindows())//win系统加载库路径//strDllPath = System.getProperty("user.dir") + "\\lib\\HCNetSDK.dll";strDllPath = "E:\\eclipse2019work\\ClientDemo-NetBeansPro\\HCNetSDK.dll";else if (osSelect.isLinux())//Linux系统加载库路径//strDllPath = System.getProperty("user.dir") + "/lib/libhcnetsdk.so";strDllPath = "/usr/local/lib/libhcnetsdk.so";hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);} catch (Exception ex) {System.out.println("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());return false;}}}return true;}/** * 播放库加载 * * @return */private static boolean CreatePlayInstance() {if (playControl == null) {synchronized (PlayCtrl.class) {String strPlayPath = "";try {if (osSelect.isWindows())//win系统加载库路径strPlayPath = System.getProperty("user.dir") + "\\lib\\PlayCtrl.dll";else if (osSelect.isLinux())//Linux系统加载库路径strPlayPath = System.getProperty("user.dir") + "/lib/libPlayCtrl.so";playControl = (PlayCtrl) Native.loadLibrary("E:\\eclipse2019work\\ClientDemo-NetBeansPro\\PlayCtrl.dll", PlayCtrl.class);} catch (Exception ex) {System.out.println("loadLibrary: " + strPlayPath + " Error: " + ex.getMessage());return false;}}}return true;} //注销设备public void videoWrite() {//退出程序时调用,每一台设备分别注销for (int id : lUserIDList) {if (hCNetSDK.NET_DVR_Logout(id)) {System.out.println("注销成功");}}lUserIDList.clear();//SDK反初始化,释放资源,只需要退出时调用一次hCNetSDK.NET_DVR_Cleanup();VideoDemo.outputStreamMap.clear();}}
package com.NetSDKDemo;import com.sun.jna.Pointer;import com.sun.jna.ptr.ByteByReference;import com.sun.jna.ptr.IntByReference;import java.io.*;import java.lang.reflect.Parameter;import java.nio.ByteBuffer;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.Timer;import static com.NetSDKDemo.ClinetDemo.hCNetSDK;import static com.NetSDKDemo.ClinetDemo.playControl;/** * 视频取流预览,下载,抓图 * * @create 2022-03-30-9:48 */public class VideoDemo implements Runnable{Timer Downloadtimer;//下载用定时器Timer Playbacktimer;//回放用定时器static FRealDataCallBack fRealDataCallBack;//预览回调函数实现//定义流的map集合static Map outputStreamMap = new HashMap();static int lPlay = -1;//预览句柄static File file;private Integer userId;private Integer iChannelNo;public VideoDemo(Integer userId, Integer iChannelNo) {this.userId = userId;this.iChannelNo = iChannelNo;}@Overridepublic void run() {fRealDataCallBack = null;if (userId == -1) {System.out.println("请先注册");return;}HCNetSDK.NET_DVR_PREVIEWINFO strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();strClientInfo.read();strClientInfo.hPlayWnd = null;//窗口句柄,从回调取流不显示一般设置为空strClientInfo.lChannel = iChannelNo;//通道号strClientInfo.dwStreamType = 0; //0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推strClientInfo.dwLinkMode = 0; //连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4- RTP/RTSP,5- RTP/HTTP,6- HRUDP(可靠传输) ,7- RTSP/HTTPS,8- NPQstrClientInfo.bBlocked = 1;strClientInfo.write();if (fRealDataCallBack == null) {fRealDataCallBack = new FRealDataCallBack();}//开启预览lPlay = hCNetSDK.NET_DVR_RealPlay_V40(userId, strClientInfo, fRealDataCallBack, null);if (lPlay == -1) {int iErr = hCNetSDK.NET_DVR_GetLastError();System.out.println("取流失败" + iErr);return;}System.out.println("取流成功");}//创建文件/** * * * @date 2022/8/31 23:37 * @param userId:登陆返回的用户句柄 */public static void setFile(int userId) {file = new File("/Download/" + new Date().getTime() + "(" + userId + ")" + ".mp4");//保存回调函数的音频数据if (!file.exists()) {try {file.createNewFile();} catch (Exception e) {e.printStackTrace();}}//FileOutputStream outputStream=new FileOutputStream(file);try {outputStreamMap.put(userId, new FileOutputStream(file));} catch (FileNotFoundException e) {e.printStackTrace();}}static class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {//预览回调public void invoke(int lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {long offset = 0;ByteBuffer buffers = pBuffer.getPointer().getByteBuffer(offset, dwBufSize);byte[] bytes = new byte[dwBufSize];buffers.rewind();buffers.get(bytes);try {//从map中取出对应的流读取数据outputStreamMap.get(lRealHandle).write(bytes);} catch (Exception e) {e.printStackTrace();}}}}

实体类

package com;/** * @author lws * @date 2022/8/31 23:07 */public class Device {private String ip;private String userName;private String passWord;public String getIp() {return ip;}public void setIp(String ip) {this.ip = ip;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassWord() {return passWord;}public void setPassWord(String passWord) {this.passWord = passWord;}}

小编的代码目录结构

简单总结:

1 初始化

2 登陆设备 并保存登陆设备的用户句柄

3 创建保存回调函数的音频文件

4 创建map集合 存储对应的文件输出流,键为用户句柄

5 对回调函数进行修改

(啊啊啊 博主给的代码有时候录制不同步!!!!博主给的代码没有视频转码代码!!! 别急啦,往下个方案看看啦。注释就都在代码里了,这里就懒得写了哈)

改造后的代码(名字就叫TwoClientDemo)

public class TwoClientDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {List devices = addDevice();TwoVideoDemo video;CameraInfo cameraInfo;FutureTask ft;for (int i = 0; i < devices.size(); i++) {cameraInfo = new CameraInfo();cameraInfo.setAddress(devices.get(i).getIp());cameraInfo.setPort((short) 8000);cameraInfo.setUserName(devices.get(i).getUserName());cameraInfo.setPwd(devices.get(i).getPassWord());video = new TwoVideoDemo(cameraInfo);ft = new FutureTask(video);new Thread(ft).start();//模拟录制过程Thread.sleep(5000);ft.get();System.out.println("取流成功");}//renderSuccess(result);}//存储登陆设备集合public static List addDevice() {List list = new ArrayList();Device device = new Device();device.setIp("192.168.1.16"); //改成自己设备的ip 用户名和密码device.setUserName("admin");device.setPassWord("Admin123");list.add(device);//Device devic1 = new Device();//device.setIp("192.168.1.17");//device.setUserName("admin");//device.setPassWord("Admin123");//list.add(devic1);return list;}}

(值得注意的一点哈!!! 方案二自己改下这里的路径)

然后线程实现类 改改 回调函数啥也别写了 直接调海康sdk接口取流即可

线程实现类

public class TwoVideoDemo implements Callable {//初始化public static final HCNetSDK INSTANCE = HCNetSDK.INSTANCE;static HCNetSDK sdk;private CameraInfo cameraInfo;public TwoVideoDemo(CameraInfo cameraInfo) {this.cameraInfo = cameraInfo;}@Overridepublic Result call() throws Exception {sdk = INSTANCE;if (!sdk.NET_DVR_Init()) {System.out.println("初始化失败..................");}//创建设备HCNetSDK.NET_DVR_DEVICEINFO_V30 deInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();//注册用户设备Integer id = sdk.NET_DVR_Login_V30(cameraInfo.getAddress(), cameraInfo.getPort(),cameraInfo.getUserName(), cameraInfo.getPwd(), deInfo);cameraInfo.setUserId(id);//判断是否注册成功if (cameraInfo.getUserId().intValue() < 0) {System.out.println("注册设备失败 错误码为:"+sdk.NET_DVR_GetLastError());} else {System.out.println("注册成功Id为:" + cameraInfo.getUserId().intValue());}//判断是否获取到设备能力HCNetSDK.NET_DVR_WORKSTATE_V30 devWork = new HCNetSDK.NET_DVR_WORKSTATE_V30();if (!sdk.NET_DVR_GetDVRWorkState_V30(cameraInfo.getUserId(), devWork)) {System.out.println("获取设备能力集失败,返回设备状态失败..............." + "错误码为:" + sdk.NET_DVR_GetLastError());}//启动实时预览功能创建clientInfo对象赋值预览参数HCNetSDK.NET_DVR_CLIENTINFO clientInfo = new HCNetSDK.NET_DVR_CLIENTINFO();clientInfo.lChannel = 1; //设置通道号clientInfo.lLinkMode = 0;//TCP取流clientInfo.sMultiCastIP = null; //不启动多播模式//创建窗口句柄clientInfo.hPlayWnd = null;FRealDataCallBack fRealDataCallBack = new FRealDataCallBack();//预览回调函数实现while (true){//开启实时预览Integer key = sdk.NET_DVR_RealPlay_V30(cameraInfo.getUserId(), clientInfo, fRealDataCallBack, null, true);//判断是否预览成功if (key.intValue() == -1) {sdk.NET_DVR_Logout(cameraInfo.getUserId());sdk.NET_DVR_Cleanup();System.out.println("预览失败 错误代码为:" + sdk.NET_DVR_GetLastError());}System.out.println("开始预览成功");//预览成功后 调用接口使视频资源保存到文件中if (!sdk.NET_DVR_SaveRealData(key, "/Download/" + new Date().getTime()+ ".mp4")) {sdk.NET_DVR_StopRealPlay(key);sdk.NET_DVR_Logout(cameraInfo.getUserId());sdk.NET_DVR_Cleanup();System.out.println("保存到文件失败 错误码为:" + sdk.NET_DVR_GetLastError());}return Result.success("录制成功",null);}}/** * @param预览回调接口实现类 */class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {@Overridepublic void invoke(int lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {}}//停止录制static void stopRecord(Integer i) {sdk.NET_DVR_Logout(i);sdk.NET_DVR_StopRealPlay(i);sdk.NET_DVR_Cleanup();}}

Result(公共返回数据类)

package com.NetSDKDemo.comm;public class Result {private boolean flag;private Integer code;private String message;private Object data;public Result() {}public Result(boolean flag, Integer code, String message, Object data) {super();this.flag = flag;this.code = code;this.message = message;this.data = data;}public Result(boolean flag, Integer code, String message) {super();this.flag = flag;this.code = code;this.message = message;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public static Result success(String msg, Object data) {return new Result(true, 200, msg, data);}public static Result error(String msg) {return new Result(false, 500, msg, null);}public static Result error(String msg, Object data,Integer code) {return new Result(true, code, msg, data);}}
CameraInfo实体类
public class CameraInfo {private String address;private String userName;private String pwd;private short port;private Integer userId;private Integer channel;private Integer key;public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}public short getPort() {return port;}public void setPort(short port) {this.port = port;}public Integer getUserId() {return userId;}public void setUserId(Integer userId) {this.userId = userId;}public Integer getChannel() {return channel;}public void setChannel(Integer channel) {this.channel = channel;}public Integer getKey() {return key;}public void setKey(Integer key) {this.key = key;}}

结果

解码工具类

public class FormatConverterUtils {/** * FFmpeg程序执行路径 * 当前系统安装好ffmpeg程序并配置好相应的环境变量后,值为ffmpeg.exe可执行程序文件在实际系统中的绝对路径 *///视频解码配置//linux下,路径肯定不是这样,是/root/xx这样子//public String outputPath = "/usr/local/tobaccocasedoc/Download/";//public String FFMPEG_PATH = "D:ffmpeg-master-latest-win64-gpl\\bin\\"; //linux中,路径为 /root/moch/ffmpeg-4.4-amd64-static/ 你自己在linux服务器上安装的路径public static String FFMPEG_PATH = "/usr/local/ffmpeg/ffmpeg-git-20220910-amd64-static/"; //linux中,路径为 /root/moch/ffmpeg-4.4-amd64-static/ 你自己在linux服务器上安装的路径/** * 音频转换器 * @param resourcePath 需要被转换的音频文件全路径带文件名 * @param targetPath 转换之后的音频文件全路径带文件名 */public static void audioConverter(String resourcePath, String targetPath) {formatConverter(new File(resourcePath), new File(targetPath), false);}/** * 视频转换器 * @param resourcePath 需要被转换的视频文件全路径带文件名 * @param targetPath 转换之后的视频文件全路径带文件名 */public static void videoConverter(String resourcePath, String targetPath) {formatConverter(new File(resourcePath), new File(targetPath), true);}/** * 文件格式转换器 * 注意!此方法为按照需求进行拼接命令来完成音频视频文件的处理 命令拼接需要根据自己需求进行更改 * 视频 或 音频 * @param fileInput 源文件路径 * @param fileOutPut 转换后的文件路径 * @param isVideo 源文件是视频文件 * */public static void formatConverter(File fileInput, File fileOutPut, boolean isVideo) {fileInput.setExecutable(true);//设置可执行权限fileInput.setReadable(true);//设置可读权限fileInput.setWritable(true);//设置可写权限fileOutPut.setExecutable(true);//设置可执行权限fileOutPut.setReadable(true);//设置可读权限fileOutPut.setWritable(true);//设置可写权限if (null == fileInput || !fileInput.exists()) {throw new RuntimeException("源文件不存在,请检查源路径");}if (null == fileOutPut) {throw new RuntimeException("转换后的路径为空,请检查转换后的存放路径是否正确");}if (!fileOutPut.exists()) {try {fileOutPut.createNewFile();} catch (IOException e) {System.out.println("转换时新建输出文件失败");}}List commond = new ArrayList();//输出直接覆盖文件commond.add("-y");commond.add("-i");commond.add(fileInput.getAbsolutePath());if (isVideo) {commond.add("-vcodec");commond.add("libx264");commond.add("-mbd");commond.add("0");commond.add("-c:a");commond.add("aac");commond.add("-s");commond.add("720*720");commond.add("-threads");//指定同时启动线程执行数, 经测试到10再大速度几无变化commond.add("25");commond.add("-preset");commond.add("ultrafast");commond.add("-strict");commond.add("-2");commond.add("-pix_fmt");commond.add("yuv420p");commond.add("-movflags");commond.add("faststart");}commond.add(fileOutPut.getAbsolutePath());//执行命令executeCommand(commond);}/** * 执行FFmpeg命令 * @param commonds 要执行的FFmpeg命令 * @return FFmpeg程序在执行命令过程中产生的各信息,执行出错时返回null */public static String executeCommand(List commonds) {if (CollectionUtils.isEmpty(commonds)) {System.out.println("--- 指令执行失败,因为要执行的FFmpeg指令为空! ---");return null;}LinkedList ffmpegCmds = new LinkedList(commonds);ffmpegCmds.addFirst(FFMPEG_PATH); // 设置ffmpeg程序所在路径System.out.println("--- 待执行的FFmpeg指令为:---" + ffmpegCmds);Runtime runtime = Runtime.getRuntime();Process ffmpeg = null;try {// 执行ffmpeg指令ProcessBuilder builder = new ProcessBuilder("/bin/sh", "-c");builder.command(ffmpegCmds);ffmpeg = builder.start();System.out.println("--- 开始执行FFmpeg指令:--- 执行线程名:" + builder.toString());// 取出输出流和错误流的信息// 注意:必须要取出ffmpeg在执行命令过程中产生的输出信息,如果不取的话当输出流信息填满jvm存储输出留信息的缓冲区时,线程就回阻塞住PrintStream errorStream = new PrintStream(ffmpeg.getErrorStream());PrintStream inputStream = new PrintStream(ffmpeg.getInputStream());errorStream.start();inputStream.start();// 等待ffmpeg命令执行完ffmpeg.waitFor();// 获取执行结果字符串String result = errorStream.stringBuffer.append(inputStream.stringBuffer).toString();// 输出执行的命令信息String cmdStr = Arrays.toString(ffmpegCmds.toArray()).replace(",", "");String resultStr = StringUtils.isBlank(result) " />

(注: 因为官方的sdk不定时更新,一些接口参数也可能发生改变 大家可以根据返回的错误码判断错误信息这篇文章仅供大家参考,如需要和我版本一致的sdk,源码下载类替换就好啦)

源码地址:(晚点上传)

对应的jar和maven需要大佬们自己去找找了

大佬勿喷!!!!!!!!!!!!!!!!!!!!! 写这个博客只是希望可以帮到大家