最近使用大华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;
}
}
文章评论