最近使用大华DVR录像机Java二次开发,使用大华录像机的Java的SDK实现以下功能:
- 设备初始化
- 登录
- 校时
- 按通道采集图片(可同步采集)
- 退出
- SDK资源清理
供有需要的参考
添加依赖
大华DVR的SDK使用了JNA,版本是5.4.0,这个可以在maven中找到,当然也可以直接使用jar,我用的是maven
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.4.0</version> </dependency>
代码
用到了其他的工具类,例如NetSDKLib.java ToolKits.java等,请到官网的SDK示例里复制。
LoginModule.java 实现初始化、登录、退出、获得设备时间、校时等功能
public class LoginModule { public static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE; public static NetSDKLib configsdk = NetSDKLib.CONFIG_INSTANCE; // 设备信息 public static NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex(); // 登陆句柄 public static NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0); private static boolean bInit = false; private static boolean bLogopen = false; /** * \if ENGLISH_LANG * Init * \else * 初始化 * \endif */ public static boolean init(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) { bInit = netsdk.CLIENT_Init(disConnect, null); if (!bInit) { System.out.println("Initialize SDK failed"); return false; } // 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK内部会自动进行重连操作 // 此操作为可选操作,但建议用户进行设置 netsdk.CLIENT_SetAutoReconnect(haveReConnect, null); //设置登录超时时间和尝试次数,可选 int waitTime = 5000; //登录请求响应超时时间设置为5S int tryTimes = 1; //登录时尝试建立链接1次 netsdk.CLIENT_SetConnectTime(waitTime, tryTimes); // 设置更多网络参数,NET_PARAM的nWaittime,nConnectTryNum成员与CLIENT_SetConnectTime // 接口设置的登录设备超时时间和尝试次数意义相同,可选 NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM(); netParam.nConnectTime = 10000; // 登录时尝试建立链接的超时时间 netParam.nGetConnInfoTime = 3000; // 设置子连接的超时时间 netsdk.CLIENT_SetNetworkParam(netParam); return true; } /** * \if ENGLISH_LANG * CleanUp * \else * 清除环境 * \endif */ public static void cleanup() { if (bLogopen) { netsdk.CLIENT_LogClose(); } if (bInit) { netsdk.CLIENT_Cleanup(); } } /** * \if ENGLISH_LANG * Login Device * \else * 登录设备 * \endif */ public static boolean login(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) { //IntByReference nError = new IntByReference(0); //入参 NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY(); pstInParam.nPort = m_nPort; pstInParam.szIP = m_strIp.getBytes(); pstInParam.szPassword = m_strPassword.getBytes(); pstInParam.szUserName = m_strUser.getBytes(); //出参 NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY(); pstOutParam.stuDeviceInfo = m_stDeviceInfo; //m_hLoginHandle = netsdk.CLIENT_LoginEx2(m_strIp, m_nPort, m_strUser, m_strPassword, 0, null, m_stDeviceInfo, nError); m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam); if (m_hLoginHandle.longValue() == 0) { System.err.printf("Login Device[%s] Port[%d]Failed. %s\n", m_strIp, m_nPort, ToolKits.getErrorCodePrint()); } else { System.out.println("Login Success [ " + m_strIp + " ]"); } return m_hLoginHandle.longValue() != 0; } /** * 同步时间 */ public static boolean syncDeviceTime() { if (m_hLoginHandle.longValue() == 0) { return false; } Calendar calendar = Calendar.getInstance(); NetSDKLib.NET_TIME netTime = new NetSDKLib.NET_TIME(); netTime.setTime(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND)); return netsdk.CLIENT_SetupDeviceTime(m_hLoginHandle, netTime); } /** * 获取时间 */ public static String getDeviceTime() { if (m_hLoginHandle.longValue() == 0) { return ""; } NetSDKLib.NET_TIME netTime2 = new NetSDKLib.NET_TIME(); netsdk.CLIENT_QueryDeviceTime(m_hLoginHandle, netTime2, 3000); return netTime2.toStringTimeEx(); } /** * \if ENGLISH_LANG * Logout Device * \else * 登出设备 * \endif */ public static boolean logout() { if (m_hLoginHandle.longValue() == 0) { return false; } boolean bRet = netsdk.CLIENT_Logout(m_hLoginHandle); if (bRet) { m_hLoginHandle.setValue(0); } return bRet; } }
CapturePictureModule.java 实现抓图
public class CapturePictureModule { /** * \if ENGLISH_LANG * Local Capture Picture * \else * 本地抓图 * \endif */ public static boolean localCapturePicture(NetSDKLib.LLong hPlayHandle, String picFileName) { if (!LoginModule.netsdk.CLIENT_CapturePictureEx(hPlayHandle, picFileName, NetSDKLib.NET_CAPTURE_FORMATS.NET_CAPTURE_JPEG)) { System.err.println("CLIENT_CapturePicture Failed!" + ToolKits.getErrorCodePrint()); return false; } else { System.out.println("CLIENT_CapturePicture success"); } return true; } /** * \if ENGLISH_LANG * Remote Capture Picture * \else * 远程抓图 * \endif */ public static boolean remoteCapturePicture(int chn) { return snapPicture(chn, 0, 0); } /** * \if ENGLISH_LANG * Timer Capture Picture * \else * 定时抓图 * \endif */ public static boolean timerCapturePicture(int chn) { return snapPicture(chn, 1, 2); } /** * \if ENGLISH_LANG * Stop Timer Capture Picture * \else * 停止定时抓图 * \endif */ public static boolean stopCapturePicture(int chn) { return snapPicture(chn, -1, 0); } /** * \if ENGLISH_LANG * Capture Picture (except local capture picture, others all call this interface) * \else * 抓图 (除本地抓图外, 其他全部调用此接口) * \endif */ private static boolean snapPicture(int chn, int mode, int interval) { // send caputre picture command to device NetSDKLib.SNAP_PARAMS stuSnapParams = new NetSDKLib.SNAP_PARAMS(); stuSnapParams.Channel = chn; // channel stuSnapParams.mode = mode; // capture picture mode stuSnapParams.Quality = 3; // picture quality stuSnapParams.InterSnap = interval; // timer capture picture time interval stuSnapParams.CmdSerial = chn; // request serial IntByReference reserved = new IntByReference(0); if (!LoginModule.netsdk.CLIENT_SnapPictureEx(LoginModule.m_hLoginHandle, stuSnapParams, reserved)) { System.err.println("CLIENT_SnapPictureEx Failed!" + ToolKits.getErrorCodePrint()); return false; } // else { // System.out.println("CLIENT_SnapPictureEx success"); // } return true; } /** * \if ENGLISH_LANG * Set Capture Picture Callback * \else * 设置抓图回调函数 * \endif */ public static void setSnapRevCallBack(NetSDKLib.fSnapRev cbSnapReceive){ LoginModule.netsdk.CLIENT_SetSnapRevCallBack(cbSnapReceive, null); } }
DHNetDeviceUtil.java 我自己写的抓图工具类,实现同步抓图。官方给的示例是异步抓图(采用的是callback回调的方式),我需要的是同步,因为我的API要执行抓图功能,并返回抓到的图,这边稍微改造了一下
public class DHNetDeviceUtil { private static final Log log = LogFactory.getLog(IndexController.class); /** * 大华录像机设备初始化(程序启动的时候运行一次) */ public static void dhInit() { LoginModule.init((lLoginID, pchDVRIP, nDVRPort, dwUser) -> { LoginModule.m_hLoginHandle = new NetSDKLib.LLong(0); log.warn("DH Device[" + pchDVRIP + "] Port[" + nDVRPort + "] DisConnected, lLoginID: " + lLoginID.longValue()); }, (lLoginID, pchDVRIP, nDVRPort, dwUser) -> { LoginModule.m_hLoginHandle = lLoginID; log.warn("DH Device[" + pchDVRIP + "] Port[" + nDVRPort + "] ReConnected, lLoginID: " + lLoginID.longValue()); }); } /** * 大华录像机设备资源清理(程序关闭时运行一次) */ public static void dhDeInit() { LoginModule.cleanup(); } public boolean capturePicture(String ip, int port, String username, String password, List<Integer> channels, File folder) { boolean login = LoginModule.m_hLoginHandle.longValue() > 0; if (!login) { login = LoginModule.login(ip, port, username, password); if (login) {//同步时间 boolean syncDeviceTimeResult = LoginModule.syncDeviceTime(); log.info("sync device time result: " + syncDeviceTimeResult + ", device time: " + LoginModule.getDeviceTime()); } } if (login) { log.info("snapPicture started, channels: " + channels.size()); for (int channel : channels) { int byChanNum = channel - 1; if (byChanNum >= 0 && byChanNum < LoginModule.m_stDeviceInfo.byChanNum) { byte[] buf = capturePicture(channel, 8); if (buf == null || buf.length == 0) { continue; } ByteArrayInputStream byteArrInput = new ByteArrayInputStream(buf); try { BufferedImage bufferedImage = ImageIO.read(byteArrInput); if (bufferedImage == null) { log.warn("snapPicture error for channel: " + channel + ", buffered image empty."); continue; } long now = System.currentTimeMillis(); // if (!useLastImage) { //CmdSerial改成了Channel File file = new File(folder, channel + "_" + now + ".jpg"); ImageIO.write(bufferedImage, "jpg", file); // if (histogramFilterData != null) { // imageDatas.put(channel, new ImageData(now, histogramFilterData)); // } log.info("snapPicture saved: " + file.getAbsolutePath() + ", file size: " + Utils.formatFileSize(buf.length)); // } } catch (IOException e) { log.error("snapPicture error for channel: " + channel + ", file size: " + Utils.formatFileSize(buf.length), e); } } } } else { log.error("error login: " + ToolKits.getErrorCodeShow()); return false; } return true; } /** * 采集图像 * @param channel 从1开始,同步返回 */ private byte[] capturePicture(int channel, int timeoutInSeconds) { LimitedTimeCondition condition = new LimitedTimeCondition(timeoutInSeconds); NetSDKLib.fSnapRev m_SnapReceiveCB = (lLoginID, pBuf, RevLen, EncodeType, CmdSerial, dwUser) -> { if (pBuf != null && RevLen > 0) { byte[] buf = pBuf.getByteArray(0, RevLen); if (buf == null || buf.length == 0) { log.warn("snapPicture error for channel: " + (CmdSerial + 1) + ", bytes empty."); } else { condition.setContent(buf); } condition.conditionWasMet(); } else { log.warn("snapPicture error for channel: " + (CmdSerial + 1) + ", bytes empty."); } condition.conditionWasMet(); }; Native.setCallbackThreadInitializer(m_SnapReceiveCB, new CallbackThreadInitializer(false, false, "snapPicture callback thread")); CapturePictureModule.setSnapRevCallBack(m_SnapReceiveCB); CapturePictureModule.remoteCapturePicture(channel - 1); if (!condition.waitForConditionToBeMet()) { log.warn("timeout while wait for capturePicture callback, channel: " + channel); } return (byte[]) condition.getContent(); } public void logout() { LoginModule.logout(); } }
使用到的LimitedTimeCondition.java 采用了CountDownLatch实现异步转同步(Java11以上)
public class LimitedTimeCondition { private final CountDownLatch conditionMetLatch; private final Integer timeoutSeconds; private Object content; public LimitedTimeCondition(final Integer timeoutSeconds) { conditionMetLatch = new CountDownLatch(1); this.timeoutSeconds = timeoutSeconds; } public boolean waitForConditionToBeMet() { try { return conditionMetLatch.await(timeoutSeconds, TimeUnit.SECONDS); } catch (final InterruptedException e) { //被打断了 return false; } } public void conditionWasMet() { conditionMetLatch.countDown(); } public Object getContent() { return content; } public void setContent(Object content) { this.content = content; } }
文章评论