赞
踩
(修改前言:最近公司需求又对接海康的人脸设备,也就刚刚好有时间重新整理写的这篇博客,看评论也有很多人说有问题 demo无法跑通 小编在这呢 也重新测一遍 也会将一些细节更详细的列出来吗 同时呢 除了之前写的这套方案,小编在这也会再提供一个方案)
写在前面:
最近也遇到了调用海康多个摄像头实现同时预览的需求,不`过官方demo里并没详细的案例,上网查了下资料,也找不到对应的解决方案 ,电话咨询海康技术,没接过,信息没回过。这里就不对海康技术支持多作评价了,废话不多说。上方案!
小编首先整理了文档和获取流程(如图)
然后小编根据这个思路将SDK的Demo流程整个看了一遍,终于发现了问题的所在,接下来直接上代码(个人是根据海康提供的demo进行简单的修改,将部分公共代码给抽了出来,另外小编只用到了预览录制和停止录制功能,解码和其他功能小编没用到,所以小编在这里都干掉了。尽量能简洁易懂就简介易懂,大佬勿喷!仅供参考和提供思路!!!!!!)
1 首先去官网下载海康的demo和文档,地址不知道?没关系 我给你啦!
https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10
选择立即下载
2 选择java开发示例,然后选择预览回放和下载
官网的demo示例就在这啦 不过 要注意官方的提示文件 根据上面的注意事项 导入文件到你的项目中
(然后就可以开始进行Demo调试啦)
具体流程如下:
1 初始化设备 并定义一个list集合存储用户句柄
-
- public static List<Integer> 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<Device> 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<Integer, FileOutputStream> 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实现预览
- @Override
- public 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- NPQ
- strClientInfo.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<Integer> 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<Device> 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<Integer, FileOutputStream> 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;
- }
-
- @Override
- public 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- NPQ
- strClientInfo.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<Device> devices = addDevice();
- TwoVideoDemo video;
- CameraInfo cameraInfo;
- FutureTask<Result> 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<Device> addDevice() {
- List<Device> 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<Result> {
-
- //初始化
- public static final HCNetSDK INSTANCE = HCNetSDK.INSTANCE;
- static HCNetSDK sdk;
-
-
- private CameraInfo cameraInfo;
-
-
- public TwoVideoDemo(CameraInfo cameraInfo) {
- this.cameraInfo = cameraInfo;
- }
- @Override
- public 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 {
-
-
- @Override
- public 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<String> commond = new ArrayList<String>();
- //输出直接覆盖文件
- 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<String> commonds) {
- if (CollectionUtils.isEmpty(commonds)) {
- System.out.println("--- 指令执行失败,因为要执行的FFmpeg指令为空! ---");
- return null;
- }
- LinkedList<String> 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) ? "【异常】" : "正常";
- System.out.println("--- 已执行的FFmepg命令: ---" + cmdStr + " 已执行完毕,执行结果: " + resultStr);
- return result;
-
- } catch (Exception e) {
- System.out.println("--- FFmpeg命令执行出错! --- 出错信息: " + e.getMessage());
- return null;
-
- } finally {
- if (null != ffmpeg) {
- ProcessKiller ffmpegKiller = new ProcessKiller(ffmpeg);
- // JVM退出时,先通过钩子关闭FFmepg进程
- runtime.addShutdownHook(ffmpegKiller);
- }
- }
- }
-
- /**
- * 用于取出ffmpeg线程执行过程中产生的各种输出和错误流的信息
- */
- static class PrintStream extends Thread {
-
- InputStream inputStream = null;
- BufferedReader bufferedReader = null;
- StringBuffer stringBuffer = new StringBuffer();
-
- public PrintStream(InputStream inputStream) {
- this.inputStream = inputStream;
- }
-
- @Override
- public void run() {
- try {
- if (null == inputStream) {
- System.out.println("--- 读取输出流出错!因为当前输出流为空!---");
- }
- bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
- String line = null;
- while ((line = bufferedReader.readLine()) != null) {
- System.out.println(line);
- stringBuffer.append(line);
- }
- } catch (Exception e) {
- System.out.println("--- 读取输入流出错了!--- 错误信息:" + e.getMessage());
- } finally {
- try {
- if (null != bufferedReader) {
- bufferedReader.close();
- }
- if (null != inputStream) {
- inputStream.close();
- }
- } catch (IOException e) {
- System.out.println("--- 调用PrintStream读取输出流后,关闭流时出错!---");
- }
- }
- }
- }
-
- /**
- * 在程序退出前结束已有的FFmpeg进程
- */
- private static class ProcessKiller extends Thread {
-
- private Process process;
-
- public ProcessKiller(Process process) {
- this.process = process;
- }
-
- @Override
- public void run() {
- this.process.destroy();
- System.out.println("--- 已销毁FFmpeg进程 --- 进程名: " + process.toString());
- }
- }
-
-
-
- /**
- * 获取音频基本信息
- *
- * @param path 文件路径|URL
- * @throws EncoderException
- */
- public static MultimediaInfo testMediaInfo(String path) throws EncoderException, MalformedURLException {
- MultimediaObject instance;
- if (path.startsWith("http")) {
- instance = new MultimediaObject(new URL(path));
- } else {
- instance = new MultimediaObject(new File(path));
- }
- return instance.getInfo();
- }
- /**
- * 原生调用ffmpeg获取音频基本信息
- *
- * @param urlPath
- */
- public static void testFFmpeg(String urlPath) {
- ProcessLocator processLocator = new DefaultFFMPEGLocator();
- ProcessWrapper ffmpeg = processLocator.createExecutor();
- ffmpeg.addArgument("-i");
- ffmpeg.addArgument(urlPath);
- try {
- ffmpeg.execute();
- String res = IOUtils.toString(ffmpeg.getErrorStream(), "UTF-8");
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- ffmpeg.destroy();
- }
- }
-
- /**
- * 转成Mp4
- *
- * @param sourceFile
- * @param distFile
- * @param pListener
- * @throws EncoderException
- */
- public static void codecToMp4(String sourceFile, String distFile, EncoderProgressListener pListener) throws EncoderException {
- File source = new File(sourceFile);
- File target = new File(distFile);
- if (target.exists()) {
- target.delete();
- }
- AudioAttributes audioAttr = new AudioAttributes();
- VideoAttributes videoAttr = new VideoAttributes();
- EncodingAttributes encodingAttr = new EncodingAttributes();
- audioAttr.setChannels(2);
- audioAttr.setCodec("aac");
- audioAttr.setBitRate(128000);
- audioAttr.setSamplingRate(44100);
- videoAttr.setCodec("libx264");
- videoAttr.setBitRate(2 * 1024 * 1024);
- videoAttr.setSize(new VideoSize(1080, 720));
- videoAttr.setFaststart(true);
- videoAttr.setFrameRate(29);
- encodingAttr.setAudioAttributes(audioAttr);
- encodingAttr.setVideoAttributes(videoAttr);
- encodingAttr.setOutputFormat("mp4");
- Encoder encoder = new Encoder();
- encoder.encode(new MultimediaObject(source), target, encodingAttr, pListener);
- }
-
-
- /**
- * 添加文字水印
- *
- * @param sourceFile
- * @param distFile
- * @param textWaterMark
- * @param pListener
- * @throws EncoderException
- */
- public static void codecToMp4WithText(String sourceFile, String distFile, String textWaterMark, EncoderProgressListener pListener) throws EncoderException {
- File sourceVideo = new File(sourceFile);
- File target = new File(distFile);
- if (target.exists()) {
- target.delete();
- }
- DrawtextFilter vf = new DrawtextFilter(textWaterMark, "(w-text_w)/2", "(h-text_h)/2", "宋体", 30.0, new Color("ffffff", "44"));
- vf.setShadow(new Color("000000", "44"), 2, 2);
- VideoAttributes videoAttributes = new VideoAttributes();
- videoAttributes.addFilter(vf);
- EncodingAttributes attrs = new EncodingAttributes();
- attrs.setVideoAttributes(videoAttributes);
- Encoder encoder = new Encoder();
- encoder.encode(new MultimediaObject(sourceVideo), target, attrs, pListener);
- }
-
- //
- public static void main(String[] args) throws EncoderException, MalformedURLException {
- String videoPath = "D:\\usr\\local\\tobaccocasedoc\\Download\\test(0).mp4";
- String wavPath = "D:\\usr\\local\\tobaccocasedoc\\Download\\test(0).mp4";
- String mp3Path = "D:\\usr\\local\\tobaccocasedoc\\Download\\result.mp4";
- //测试获取视频信息
- MultimediaInfo info = testMediaInfo(videoPath);
- System.out.println(JSON.toJSONString(info));
- //测试音频转码
- codecToMp4(wavPath, mp3Path, new EncoderProgressListener() {
- @Override
- public void sourceInfo(MultimediaInfo info) {
- System.out.println(JSON.toJSONString(info));
- }
- @Override
- public void progress(int permil) {
- System.out.println(permil);
- }
- @Override
- public void message(String message) {
- System.out.println(message);
- }
- });
-
- }
(注: 因为官方的sdk不定时更新,一些接口参数也可能发生改变 大家可以根据返回的错误码判断错误信息 这篇文章仅供大家参考,如需要和我版本一致的sdk,源码下载类替换就好啦)
源码地址:(晚点上传)
对应的jar和maven需要大佬们自己去找找了
大佬勿喷!!!!!!!!!!!!!!!!!!!!! 写这个博客只是希望可以帮到大家
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。