最近半個月在對接大華攝像頭水评,要實現(xiàn)攝像頭的實時預(yù)覽、云臺控制和截圖等功能媚送。在網(wǎng)上找了很多資源也沒有找到想要的中燥,所以自己寫了工具類。簡單陳述一下基于大華的官網(wǎng)SDK實現(xiàn)攝像頭的云臺控制和抓圖塘偎。無非就是調(diào)用SDK提供的一些函數(shù)來實現(xiàn)疗涉。
1. 首先先看看大華給的開發(fā)流程
image.png
開發(fā)可以順著這個流程圖的步驟,就會變得簡單明了吟秩,開發(fā)其他功能也一樣咱扣,下面是我封裝好的工具類。至于一些引用類都是在大華官網(wǎng)的SDK里面涵防。
- 云臺控制
/**
* @author : colors
* @date : 11:04 2020/12/3
* 云臺控制工具類
*/
public class PTZControlUtil {
// 初始化sdk
public static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
// 設(shè)備信息
private static NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
// 登陸句柄
private static NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0);
// 網(wǎng)絡(luò)斷線處理
private static DisConnect disConnect = new DisConnect();
// 設(shè)備連接恢復(fù)闹伪,實現(xiàn)設(shè)備連接恢復(fù)接口
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);
}
// 開始向上移動,若超過角度則會變?yōu)樽笥乙苿? 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);
//入?yún)? 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;
}
// 設(shè)備斷線回調(diào): 通過 CLIENT_Init 設(shè)置該回調(diào)函數(shù)王凑,當設(shè)備出現(xiàn)斷線時忿薇,SDK會調(diào)用該函數(shù)
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);
}
}
// 網(wǎng)絡(luò)連接恢復(fù)左电,設(shè)備重連成功回調(diào)
// 通過 CLIENT_SetAutoReconnect 設(shè)置該回調(diào)函數(shù)陷遮,當已斷線的設(shè)備重連成功時疗韵,SDK會調(diào)用該函數(shù)
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. 抓圖功能
先看看官網(wǎng)給的流程圖瓣赂,步驟3 看自己需求來調(diào)用函數(shù)
image.png
下面是封裝好的工具類慧域,可直接食用鲤竹。
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 設(shè)備ip
* @param m_nPort 設(shè)備端口
* @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, 防止設(shè)備斷線時抓拍回調(diào)沒有被觸發(fā),而導致死等
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) {
//入?yún)? 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();
}
}
}
}
/**
* 抓圖回調(diào)函數(shù)
*
* @param cbSnapReceive
*/
private static void setSnapRevCallBack(NetSDKLib.fSnapRev cbSnapReceive) {
netsdk.CLIENT_SetSnapRevCallBack(cbSnapReceive, null);
}
// 設(shè)備斷線回調(diào): 通過 CLIENT_Init 設(shè)置該回調(diào)函數(shù)碘橘,當設(shè)備出現(xiàn)斷線時,SDK會調(diào)用該函數(shù)
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);
}
}
// 網(wǎng)絡(luò)連接恢復(fù)吱肌,設(shè)備重連成功回調(diào)
// 通過 CLIENT_SetAutoReconnect 設(shè)置該回調(diào)函數(shù)痘拆,當已斷線的設(shè)備重連成功時,SDK會調(diào)用該函數(shù)
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);
}
}
結(jié)果:
image.png
更新
記錄一下踩到的坑:大華SDK部署在windows 環(huán)境和 Linux環(huán)境不一樣氮墨,在Linux環(huán)境中需要從環(huán)境變量里面去加載庫文件且 NetSDKLib
接口中的 fDisConnect
繼承的類不一樣纺蛆,需要更換,不然會報異常:lang.IllegalArgumentException:Invalid calling convention 63规揪。
注意資源的釋放桥氏、登錄和登出。
歡迎留言交流粒褒,你的關(guān)注是對我最大的支持识颊。