最近半个月在对接大华摄像头,要实现摄像头的实时预览、云台控制和截图等功能。在网上找了很多资源也没有找到想要的,所以自己写了工具类。简单陈述一下基于大华的官网SDK实现摄像头的云台控制和抓图。无非就是调用SDK提供的一些函数来实现。
1. 首先先看看大华给的开发流程

开发可以顺着这个流程图的步骤,就会变得简单明了,开发其他功能也一样,下面是我封装好的工具类。至于一些引用类都是在大华官网的SDK里面。
- 云台控制
/**
* @author : colors
* @date : 11:04 2020/12/3
* 云台控制工具类
*/
public class PTZControlUtil {
// 初始化sdk
public static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
// 设备信息
private static NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
// 登陆句柄
private static NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0);
// 网络断线处理
private static DisConnect disConnect = new DisConnect();
// 设备连接恢复,实现设备连接恢复接口
private static HaveReConnect haveReConnect = new HaveReConnect();
/**
* 云台控制
* 向上移动
*
* @param m_strIp ip
* @param m_nPort 端口
* @param m_strUser 登录名
* @param m_strPassword 密码
* @param nChannelID 通道id 默认为0
* @param lParam1 默认 0,当有左上或左下等操作时才会传值 (1-8)
* @param lParam2 垂直/水平 移动速度 (1-8)
*/
public static void upControlPtz(String m_strIp, int m_nPort, String m_strUser, String m_strPassword, int nChannelID, int lParam1, int lParam2) {
// 初始化
LoginModule.init(disConnect, haveReConnect);
// 若未登录,先登录。
if (m_hLoginHandle.longValue() == 0) {
login(m_strIp, m_nPort, m_strUser, m_strPassword);
}
// 开始向上移动,若超过角度则会变为左右移动
if (m_hLoginHandle.longValue() != 0) {
startUpControl(nChannelID, lParam1, lParam2);
}
System.out.println("操作完成");
// 停止移动
stopUpControl(nChannelID);
// 退出
logout();
System.out.println("退出登录...");
// 释放资源
LoginModule.cleanup();
}
// 向上
private static void startUpControl(int nChannelID, int lParam1, int lParam2) {
if (m_hLoginHandle.longValue() != 0) {
netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, nChannelID,
NetSDKLib.NET_PTZ_ControlType.NET_PTZ_UP_CONTROL,
lParam1, lParam2, 0, 0);
} else {
System.err.println("登录句柄不存在!");
}
}
private static void stopUpControl(int nChannelID) {
netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, nChannelID,
NetSDKLib.NET_PTZ_ControlType.NET_PTZ_UP_CONTROL,
0, 0, 0, 1);
}
// 上下左右等其他一些操作只是指令不同 ,此处就省略了
/**
* 登录
*
* @param m_strIp ip
* @param m_nPort 端口号
* @param m_strUser 账号
* @param m_strPassword 密码
* @return 成功则 true
*/
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_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
System.out.println(netsdk.getClass());
if (m_hLoginHandle.longValue() == 0) {
System.err.printf("登录失败!\n", m_strIp, m_nPort, ToolKits.getErrorCodePrint());
} else {
System.out.println("登录成功: [ " + m_strIp + " ]");
}
return m_hLoginHandle.longValue() == 0 ? false : true;
}
/**
* 退出登录
*/
private 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;
}
// 设备断线回调: 通过 CLIENT_Init 设置该回调函数,当设备出现断线时,SDK会调用该函数
private static class DisConnect implements NetSDKLib.fDisConnect {
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("Device[%s] Port[%d] DisConnect!\n", pchDVRIP, nDVRPort);
}
}
// 网络连接恢复,设备重连成功回调
// 通过 CLIENT_SetAutoReconnect 设置该回调函数,当已断线的设备重连成功时,SDK会调用该函数
private static class HaveReConnect implements NetSDKLib.fHaveReConnect {
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("ReConnect Device[%s] Port[%d]\n", pchDVRIP, nDVRPort);
}
}
2. 抓图功能
先看看官网给的流程图,步骤3 看自己需求来调用函数

下面是封装好的工具类,可直接食用。
public class CapturePictureUtil {
private static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
private static NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
private static NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0);
private static DisConnect disConnect = new DisConnect();
private static HaveReConnect haveReConnect = new HaveReConnect();
private static fCaptureReceiveCB m_CaptureReceiveCB = new fCaptureReceiveCB();
/**
* 封装抓图方法
*
* @param m_strIp 设备ip
* @param m_nPort 设备端口
* @param m_strUser 账号
* @param m_strPassword 密码
* @param chn 通道id
* @param mode 请求一帧 默认 0
* @param interval 时间单位秒:默认 0
*/
public static void capturePicture(String m_strIp, int m_nPort, String m_strUser, String m_strPassword, int chn, int mode, int interval) {
// 初始化sdk
LoginModule.init(disConnect, haveReConnect);
// 登录
if (m_hLoginHandle.longValue() == 0) {
login(m_strIp, m_nPort, m_strUser, m_strPassword);
}
// 截图
if (m_hLoginHandle.longValue() != 0) {
snapPicture(chn, mode, interval);
}
try {
synchronized (fCaptureReceiveCB.class) {
// 默认等待 3s, 防止设备断线时抓拍回调没有被触发,而导致死等
fCaptureReceiveCB.class.wait(3000L);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("--> " + Thread.currentThread().getName() + " CLIENT_SnapPictureEx Success." + System.currentTimeMillis());
logout();
LoginModule.cleanup();
}
/*
* 登录
*/
public static boolean login(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) {
//入参
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_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
System.out.println(netsdk.getClass());
if (m_hLoginHandle.longValue() == 0) {
System.err.printf("登录失败!\n", m_strIp, m_nPort, ToolKits.getErrorCodePrint());
} else {
System.out.println("登录成功: [ " + m_strIp + " ]");
}
return m_hLoginHandle.longValue() == 0 ? false : true;
}
/**
* 退出登录
*/
private 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;
}
/**
* 抓图方法
*
* @param chn 通道号
* @param mode 默认 0
* @param interval 默认0
* @return 成功返回ture
*/
private static boolean snapPicture(int chn, int mode, int interval) {
setSnapRevCallBack(m_CaptureReceiveCB);
// 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 = 0; // request serial
IntByReference reserved = new IntByReference(0);
if (!netsdk.CLIENT_SnapPictureEx(m_hLoginHandle, stuSnapParams, reserved)) {
System.err.printf("CLIENT_SnapPictureEx Failed!" + ToolKits.getErrorCodePrint());
return false;
} else {
System.out.println("CLIENT_SnapPictureEx success");
}
return true;
}
/**
* 保存图片
*/
private static class fCaptureReceiveCB implements NetSDKLib.fSnapRev {
BufferedImage bufferedImage = null;
public void invoke(NetSDKLib.LLong lLoginID, Pointer pBuf, int RevLen, int EncodeType, int CmdSerial, Pointer dwUser) {
if (pBuf != null && RevLen > 0) {
String strFileName = SavePath.getSavePath().getSaveCapturePath();
System.out.println("strFileName = " + strFileName);
byte[] buf = pBuf.getByteArray(0, RevLen);
ByteArrayInputStream byteArrInput = new ByteArrayInputStream(buf);
try {
bufferedImage = ImageIO.read(byteArrInput);
if (bufferedImage == null) {
return;
}
ImageIO.write(bufferedImage, "jpg", new File(strFileName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 抓图回调函数
*
* @param cbSnapReceive
*/
private static void setSnapRevCallBack(NetSDKLib.fSnapRev cbSnapReceive) {
netsdk.CLIENT_SetSnapRevCallBack(cbSnapReceive, null);
}
// 设备断线回调: 通过 CLIENT_Init 设置该回调函数,当设备出现断线时,SDK会调用该函数
private static class DisConnect implements NetSDKLib.fDisConnect {
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("Device[%s] Port[%d] DisConnect!\n", pchDVRIP, nDVRPort);
}
}
// 网络连接恢复,设备重连成功回调
// 通过 CLIENT_SetAutoReconnect 设置该回调函数,当已断线的设备重连成功时,SDK会调用该函数
private static class HaveReConnect implements NetSDKLib.fHaveReConnect {
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("ReConnect Device[%s] Port[%d]\n", pchDVRIP, nDVRPort);
}
}
结果:

更新
记录一下踩到的坑:大华SDK部署在windows 环境和 Linux环境不一样,在Linux环境中需要从环境变量里面去加载库文件且 NetSDKLib
接口中的 fDisConnect
继承的类不一样,需要更换,不然会报异常:lang.IllegalArgumentException:Invalid calling convention 63。
注意资源的释放、登录和登出。
欢迎留言交流,你的关注是对我最大的支持。
网友评论