海康摄像头SDK在Linux、windows下的兼容问题sdk
零、前⾔
最近⼀直在做⼈脸识别相关的应⽤。
主要就是使⽤海康的摄像头抓拍、录制视频,使⽤虹软的sdk进⾏⼈脸识别,使⽤jna调⽤这些sdk。
海康的sdk在使⽤时遭遇了很多问题,主要问题就是window下开发,Linux下部署,但是海康在Linux、win下的sdk并不⼀致,导致出现在win上开发运⾏好好的,Linux下部署时却不⾏了。
解决了这些问题后,迷惑的⼜出现了⼀个问题:
同样配置(cpu都⼀样)、同样系统(centos 7.8)的机器,只是⼚家不⼀样,但是其中⼀个机器可以布防海康摄像头,另⼀个就不⾏,懵逼了。
写此⽂章⼀⽅⾯是解决⼀些后来的兄弟们可能会碰到的问题,⼀⽅⾯是看看有没有兄弟们碰到过这个问题,怎么解决的。
⼀、环境依赖
使⽤海康sdk最重要的是sdk路径问题。
但是有个需要注意的点:
海康sdk在win、Linux下的sdk名称不⼀致
配置⽂件:
# 海康windows的SDK name
hksdk.name.win=HCNetSDK
# 海康Linux的SDK name
hksdk.name.linux=hcnetsdk
1.1 w i n
windows下⽐较简单,代码加载时添加路径就⾏了。
String sdkHome = "F:\home\dll\hk-win\";
String sdkName = ("hksdk.name.win");
HCNetSDKI INSTANCE = (HCNetSDKI) Native.loadLibrary(sdkHome + "HCNetSDK",
HCNetSDKI.class);
1.2 li nux
Linux代码加上路径则没有⽤,需要将官⽹下载的.so⽂件放⼊Linux的配置⽂件中。
HkSdkLinux INSTANCE = (HkSdkLinux) Native.loadLibrary("hcnetsdk",
HkSdkLinux.class);
上传dll⽂件⾄/home/deploy/dll/hk-linux下。
写了个脚本,也可以单独拿出来执⾏
vim loadDll.sh
#!/bin/bash
echo 'export LD_LIBRAY_PATH=$LD_LIBRAY_PATH:/home/deploy/dll/hk-linux:/home/deploy/dll/hk-linux/HCNetSDKCom' >> /etc/profile
source /etc/profile
cat /etc/profile
echo '/home/deploy/dll/hk-linux' >> /etc/f
echo '/home/deploy/dll/hk-linux/HCNetSDKCom' >> /etc/f
ldconfig
cat /etc/f
chmod +x *.sh
./loadDll.sh
1.3 jna依赖
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
<version>3.0.9</version>
</dependency>
⼆、创建海康Linux、win下的sdk调⽤⽅法
2.1 创建sdk接⼝
import com.sun.jna.Library;
/**
* 为了海康win、Linux的整合写的接⼝类
*/
public interface IHkSdkInterface extends Library {
/
/ 初始化sdk
boolean NET_DVR_Init();
// 设置连接时间与重连时间
boolean NET_DVR_SetConnectTime(int i, int i1);
boolean NET_DVR_SetReconnect(int i, boolean b);
// 登录接⼝
int NET_DVR_Login_V40(HCNetSDKI.NET_DVR_USER_LOGIN_INFO m_strLoginInfo, HCNetSDKI.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo);
// 获取错误号
int NET_DVR_GetLastError();
// 回调服务
void NET_DVR_SetDVRMessageCallBack_V31(IMSGCallBackInterface msgCallBack, Object o);
// 布防监控
int NET_DVR_SetupAlarmChan_V41(int lUserID, ISetupAlarmInterface struAlarmParam);
// 布防撤销
boolean NET_DVR_CloseAlarmChan_V30(int handle);
// 退出
boolean NET_DVR_Logout(int userId);
// 释放sdk
boolean NET_DVR_Cleanup();
}
为什么要创建这么⼀个接⼝,因为海康这个SDK在win、Linux下继承的类不是同⼀个。
可以看到上⾯这个接⼝继承了⼀个 Library 接⼝。
⽽win下的sdk类则不只需要继承Library,还要继承StdCallLibrary,如下
public interface HCNetSDKI extends IHkSdkInterface, StdCallLibrary {
……
}
但是Linux则不能继承StdCallLibrary,否则会报错。
2.2 创建抓拍回调接⼝
mport com.sun.jna.Callback;
import com.sun.jna.Pointer;
/**
* 为海康win、Linux的sdk布防回调所使⽤
* date:2020-08-25
* author:yaowei
*/
public interface IMSGCallBackInterface extends Callback {
public boolean invoke(int lCommand,IDvrAlarmer pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser);
}
2.3 创建布防接⼝
/**
* 为了海康win、linux sdk布防使⽤
*/
public class ISetupAlarmInterface extends Structure {
public int dwSize;
public byte byLevel; //布防优先级,0-⼀等级(⾼),1-⼆等级(中),2-三等级(低)
public byte byAlarmInfoType; //上传报警信息类型(抓拍机⽀持),0-⽼报警信息(NET_DVR_PLATE_RESULT),1-新报警信息(NET_ITS_PLATE_RESULT)2012-9-28
public byte byRetAlarmTypeV40; //0--返回NET_DVR_ALARMINFO_V30或NET_DVR_ALARMINFO, 1--设备⽀持NET_DVR_ALARMINFO_V40则返回NET_DVR_ALARMINFO_V40,不⽀持则返回NET_DVR_ALARMINFO_V30或NET_DVR_ALA    public byte byRetDevInfoVersion; //CVR上传报警信息回调结构体版本号 0-COMM_ALARM_DEVICE, 1-COMM_ALARM_DEVICE_V40
public byte byRetVQDAlarmType; //VQD报警上传类型,0-上传报报警NET_DVR_VQD_DIAGNOSE_INFO,1-上传报警NET_DVR_VQD_ALARM
public byte byFaceAlarmDetection;
public byte bySupport;
public byte byBrokenNetHttp;
public short wTaskNo;    //任务处理号和 (上传数据NET_DVR_VEHICLE_RECOG_RESULT中的字段dwTaskNo对应同时下发任务结构 NET_DVR_VEHICLE_RECOG_COND中的字段dwTaskNo对应)
public byte byDeployType;    //布防类型:0-客户端布防,1-实时布防
public byte[] byRes1 = new byte[3];
public byte byAlarmTypeURL;//bit0-表⽰⼈脸抓拍报警上传(INTER_FACESNAP_RESULT);0-表⽰⼆进制传输,1-表⽰URL传输(设备⽀持的情况下,设备⽀持能⼒根据具体报警能⼒集判断,同时设备需要⽀持URL的相关服务,当前是”云存储“    public byte byCustomCtrl;//Bit0- 表⽰⽀持副驾驶⼈脸⼦图上传: 0-不上传,1-上传,(注:只在公司内部8600/8200等平台开放)
}
2.4 w i n S D K
//SDK接⼝说明,HCNetSDK.dll
public interface HCNetSDKI extends IHkSdkInterface, StdCallLibrary {
//    String DLL_DIR = "F:\\home\\dll\\hk-win" + File.separator;
String DLL_DIR = HkSdkHome() ;
/
/ sdk加载路径
String sdk = DLL_DIR + HkSdkName();
HCNetSDKI INSTANCE = (HCNetSDKI) Native.loadLibrary(sdk,
HCNetSDKI.class);
……
代码太长,复制海康sdk就⾏了
}
2.5 L i nux S D K
//SDK接⼝说明,HCNetSDK.so
public interface HkSdkLinux extends IHkSdkInterface {
HkSdkLinux INSTANCE = (HkSdkLinux) Native.loadLibrary("hcnetsdk",
HkSdkLinux.class);
……
}
2.6 回调函数
注册布防后需要有个回调函数进⾏操作,可以在这⾥得到摄像头抓拍的图⽚后进⾏业务操作。
@Service
public class MSGCallBack implements IMSGCallBackInterface {
@Override
public boolean invoke(int lCommand, IDvrAlarmer pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser)
{
AlarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);
return true;
}
public void AlarmDataHandle(int lCommand, IDvrAlarmer pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser)
{
//lCommand是传的报警类型
switch (lCommand)
{
case HCNetSDKI.COMM_UPLOAD_FACESNAP_RESULT:
//实时⼈脸抓拍上传
HCNetSDKI.NET_VCA_FACESNAP_RESULT strFaceSnapInfo = new HCNetSDKI.NET_VCA_FACESNAP_RESULT();
strFaceSnapInfo.write();
Pointer pFaceSnapInfo = Pointer();
pFaceSnapInfo.write(0, ByteArray(0, strFaceSnapInfo.size()), 0, strFaceSnapInfo.size());
sAlarmType = sAlarmType + ":⼈脸抓拍上传,⼈脸评分:" + strFaceSnapInfo.dwFaceScore + ",年龄段:" + strFaceSnapInfo.struFeature.byAgeGroup + ",性别:" + strFaceSnapInfo.struFeature.bySex;                newRow[0] = imgName;
//报警类型
newRow[1] = sAlarmType;
//报警设备IP地址
sIP = new String(strFaceSnapInfo.struDevInfo.struDevIP.sIpV4).split("\0", 2);
newRow[2] = sIP[0];
System.out.println( "today: " + newRow[0] + ",报警类型:" + newRow[1] + ",sIP:" + sIP);
……
break;
default:
newRow[0] = dateFormat.format(today);
//报警类型
newRow[1] = sAlarmType;
//报警设备IP地址
sIP = new String(pAlarmer.sDeviceIP).split("\0", 2);
newRow[2] = sIP[0];
break;
}
// 没有拍到照⽚
if (StringUtil.isNullOrNone(smallImgPath)) {
return;
}
// ⽐对⼈脸,开门
……
}
}
三、注册、登录、布防
不再另外说明了,需要说的都在注释⾥。
流程就是初始化SDK、注册登录、获取摄像头句柄、布防监控、注册回调函数。
⼈脸识别我就写在回调函数中。
3.1 创建海康sdk的服务类
@Service
public class HkFaceSdk {
private static final org.slf4j.Logger Logger = Logger(HkFaceSdk.class);
private IHkSdkInterface hCNetSDK;// 海康sdk
//    private IMSGCallBackInterface msgCallBack;// 布防监控回调函数
@Autowired
public MSGCallBack msgCallBack;//报警回调函数实现
@Value("${client.heart.timeout}")
int heartTime;
// ⼼跳⽂件夹
@Value("${tcp.heart.folder}")
String heartFolder;
//  ⼼跳⽂件后缀
@Value("${tcp.heart.fix}")
String heartFix;
@Autowired
RedisService redisService;
public static void main(String[] args)  {
HkFaceSdk faceDemo = new HkFaceSdk();
faceDemo.faceMain("192.168.1.203","admin","fskj123456");
}
……
注册、布防、监听、撤销、退出
}
sdk初始化
3.2 sdk初始化
public void initSdk() {
if (OSinfo.isWindows()) {
// windows 注册登录、布防撤防
hCNetSDK = HCNetSDKI.INSTANCE;
} else {
// Linux 版本海康sdk布防
hCNetSDK = HkSdkLinux.INSTANCE;
}
boolean initBool = hCNetSDK.NET_DVR_Init();
if (!initBool) {
<("初始化海康SDK失败");
}
//设置连接时间与重连时间
hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
hCNetSDK.NET_DVR_SetReconnect(10000, true);
Logger.info("初始化海康SDK成功");
}
注册
3.3 注册
/**
* 注册海康sdk
* @param m_sDeviceIP ip地址
* @param m_sUsername ⽤户名
* @param m_sPassword 密码
*/
private void register(String m_sDeviceIP,String m_sUsername,String m_sPassword) {
HCNetSDKI.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDKI.NET_DVR_USER_LOGIN_INFO();//设备登录信息
HCNetSDKI.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDKI.NET_DVR_DEVICEINFO_V40();//设备信息
//---------------------------------------
//登录参数,包括设备地址、登录⽤户、密码等
//注册之前先注销已注册的⽤户,预览情况下不可注销
int userId = -1;
//        int userId = Int(EasyConstants.HK_SDK.USER + m_sDeviceIP);
//        if (userId > -1) {
//            //先注销
//            loginOut(userId);
//            ve(EasyConstants.HK_SDK.USER + m_sDeviceIP);
//        }
//注册
m_strLoginInfo.sDeviceAddress = new byte[HCNetSDKI.NET_DVR_DEV_ADDRESS_MAX_LEN];
System.arraycopy(Bytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());
m_strLoginInfo.sUserName = new byte[HCNetSDKI.NET_DVR_LOGIN_USERNAME_MAX_LEN];
System.arraycopy(Bytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());
m_strLoginInfo.sPassword = new byte[HCNetSDKI.NET_DVR_LOGIN_PASSWD_MAX_LEN];
System.arraycopy(Bytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length())
;
m_strLoginInfo.wPort = (short)8000;// 设备端⼝,默认8000
m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是
m_strLoginInfo.write();
userId = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
// userId存⼊缓存
redisService.put(EasyConstants.HK_SDK.USER + m_sDeviceIP,userId);
if (userId < 0) {
Logger.info("海康SDK注册失败,ip:{},userId:{},错误号:{}",m_sDeviceIP, userId, hCNetSDK.NET_DVR_GetLastError());
} else {
//            JOptionPane.showMessageDialog(null, "注册成功");
Logger.info("海康SDK注册成功,ip:{},userId:{}",m_sDeviceIP,userId);
// 注册成功后,直接布防
setupAlarmChan(m_sDeviceIP);
}
}
布防
3.4 布防
/**
* 布防
*/
public void setupAlarmChan(String ip) {
if (msgCallBack == null) {
msgCallBack = new MSGCallBack();
//            if (OSinfo.isWindows()) {
//                msgCallBack = new MSGCallBack();
//            } else {
//                msgCallBack = new MSGCallBackLinux();
//            }
}
//设置报警回调函数
hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(msgCallBack, null);
//启⽤布防
/
/        HCNetSDKI.NET_DVR_SETUPALARM_PARAM struAlarmParam = new HCNetSDKI.NET_DVR_SETUPALARM_PARAM();
//        if (OSinfo.isWindows()) {
//            struAlarmParam = new HCNetSDKI.NET_DVR_SETUPALARM_PARAM();
//        }
//启⽤布防
ISetupAlarmInterface struAlarmParam = new HkSdkLinux.NET_DVR_SETUPALARM_PARAM();
if (OSinfo.isWindows()) {
struAlarmParam = new HCNetSDKI.NET_DVR_SETUPALARM_PARAM();
}
struAlarmParam.dwSize = struAlarmParam.size();
struAlarmParam.byFaceAlarmDetection = 1; //⼈脸侦测报警,设备⽀持⼈脸侦测功能的前提下,上传COMM_ALARM_FACE_DETECTION类型报警信息
//        struAlarmParam.byLevel=1;//智能交通布防优先级:0- ⼀等级(⾼),1- ⼆等级(中),2- 三等级(低)
//        struAlarmParam.byAlarmInfoType=1;//智能交通报警信息上传类型:0- ⽼报警信息(NET_DVR_PLATE_RESULT),1- 新报警信息(NET_ITS_PLATE_RESULT) //        struAlarmParam.byDeployType =1; //布防类型(仅针对门禁主机、⼈证设备):0-客户端布防(会断⽹续传),1-实时布防(只上传实时数据)
//        struAlarmParam.write();
int lUserID = Int(EasyConstants.HK_SDK.USER + ip);//⽤户id
// 布防接⼝ V41
int lHandle = hCNetSDK.NET_DVR_SetupAlarmChan_V41(lUserID,struAlarmParam);// ⽤户句柄
if (lHandle < 0) {
<("布防失败,未取得摄像头句柄 ip:{},userId:{},lHandle:{},error status: {}",ip,lUserID,lHandle, hCNetSDK.NET_DVR_GetLastError());
loginOutByIp(ip);
return;
}
Logger.info("布防海康机成功,ip:{},userId:{},lHandle:{}",ip,lUserID,lHandle);
new Thread(() -> {
listenHandle(ip,lHandle);
}).start();
}
监听
3.5 监听
如果摄像头掉线了,就退出登录
这⾥好像有点问题,如果⽹络中断后⼜重连怎么办?这⾥还没测试,回头应该要优化下。
/**
* 每30秒检测⼀次
* @param ip  摄像头ip
* @param lHandle 摄像头SDK句柄
*/
//    @Scheduled(fixedRate = 1000 * 30    )
public void listenHandle(String ip ,int lHandle) {
while (lHandle > -1) {
if (lHandle < 0) {
<("NET_DVR_SetupAlarmChan_V41 error: {}", hCNetSDK.NET_DVR_GetLastError());
loginOutByIp(ip);
return;
}
// 存储⼼跳
saveHeart(ip);
try {
// 每30秒监听⼀次
Thread.sleep(heartTime * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
撤销布防
3.6 撤销布防
/**
* 撤销布防
*/
public void closeAlarmChan(String ip) {
int handle = (int) Int(EasyConstants.HK_SDK.HANDLE + ip);
//撤销布防上传通道
if (!hCNetSDK.NET_DVR_CloseAlarmChan_V30(handle))
{
<("撤防失败,NET_DVR_CloseAlarmChan_V30 error:{}", hCNetSDK.NET_DVR_GetLastError());
loginOutByIp(ip);
return;
}
Logger.info("撤防成功,ip:{}",ip);
}
3.7 注销
/**
* 注销
*/
public void loginOut(int userId) {
//注销⽤户
hCNetSDK.NET_DVR_Logout(userId);
//释放SDK资源
hCNetSDK.NET_DVR_Cleanup();
Logger.info("loginOut 已注销,退出,userId:{}",userId);
}
public void loginOutByIp(String ip) {
int userId = Int(EasyConstants.HK_SDK.USER + ip);//⽤户句柄
loginOut(userId);
redisService.put(EasyConstants.HK_SDK.USER + ip,-1);
Logger.info("loginOutByIp 已注销,退出 ip:{}",ip);
}
四、后记
还是开头那个问题
同样配置(cpu都⼀样)、同样系统(centos 7.8)的机器,只是⼚家不⼀样,但是其中⼀个机器可以布防海康摄像头,另⼀个就不⾏,懵逼了。
写此⽂章⼀⽅⾯是解决⼀些后来的兄弟们可能会碰到的问题,⼀⽅⾯是看看有没有兄弟们碰到过这个问题,怎么解决的。
错误如下
海康SDK注册失败,ip:192.168.1.203,userId:-1,错误号:29
15:06:44.218 [main] INFO  com.fsiot.face.hksdk.HkFaceSdk - 海康SDK注册失败,ip:192.168.1.12,
userId:-1,错误号:29
唉!
五、附录 海康错误号
⽹络通讯库错误码
错误类型错误值错误信息
NET_DVR_NOERROR 0 没有错误。
NET_DVR_PASSWORD_ERROR 1 ⽤户名密码错误。注册时输⼊的⽤户名或者密码错误。
NET_DVR_NOENOUGHPRI 2 权限不⾜。⼀般和通道相关,例如有预览通道1权限,⽆预览通道2权限,即有预览权限但不完全,预览通道2返回此错误。
NET_DVR_NOINIT 3 SDK未初始化。必须先调⽤NET_DVR_Init,该接⼝是调⽤其他SDK函数的前提且⼀个程序进程只需要调⽤⼀次。
NET_DVR_CHANNEL_ERROR 4 通道号错误。设备通道分模拟通道和数字通道(IP通道),NET_D
VR_Login_V40登录设备成功之后会返回设备⽀持的通道个数和起始通道号取值,详见“通道和通道号号相关说明”。NET_DVR_OVER_MAXLINK 5 设备总的连接数超过最⼤。例如⽹络摄像机只⽀持6路预览,预览第7路即会返回失败,错误码返回5。不同设备性能不⼀样,⽀持路数也不同。
NET_DVR_VERSIONNOMATCH 6 版本不匹配。SDK和设备的版本不匹配。
NET_DVR_NETWORK_FAIL_CONNECT 7 连接设备失败。设备不在线或⽹络原因引起的连接超时等。
NET_DVR_NETWORK_SEND_ERROR 8 向设备发送失败。
NET_DVR_NETWORK_RECV_ERROR 9 从设备接收数据失败。
NET_DVR_NETWORK_RECV_TIMEOUT 10 从设备接收数据超时。
NET_DVR_NETWORK_ERRORDATA 11 传送的数据有误。发送给设备或者从设备接收到的数据错误,如远程参数配置时输⼊设备不⽀持的值。
NET_DVR_ORDER_ERROR 12 调⽤次序错误。
NET_DVR_OPERNOPERMIT 13 ⽆此权限。⽤户对某个功能模块的权限,例如⽆预览权限⽤户预览返回此错误。
NET_DVR_COMMANDTIMEOUT 14 设备命令执⾏超时。
NET_DVR_ERRORSERIALPORT 15 串⼝号错误。指定的设备串⼝号不存在。
NET_DVR_ERRORALARMPORT 16 报警端⼝错误。指定的设备报警输⼊或者输出端⼝不存在。
NET_DVR_PARAMETER_ERROR 17 参数错误。SDK接⼝中给⼊的输⼊或输出参数为空,或者参数格式或值不符合要求。
NET_DVR_CHAN_EXCEPTION 18 设备通道处于错误状态
NET_DVR_NODISK 19 设备⽆硬盘。当设备⽆硬盘时,对设备的录像⽂件、硬盘配置等操作失败。
NET_DVR_ERRORDISKNUM 20 硬盘号错误。当对设备进⾏硬盘管理操作时,指定的硬盘号不存在时返回该错误。
NET_DVR_DISK_FULL  21 设备硬盘满。
NET_DVR_DISK_ERROR  22 设备硬盘出错
NET_DVR_NOSUPPORT 23 设备不⽀持。
NET_DVR_BUSY 24 设备忙。
NET_DVR_MODIFY_FAIL 25 设备修改不成功。
NET_DVR_PASSWORD_FORMAT_ERROR 26 密码输⼊格式不正确

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。