前言:
以下内容都是自己最近做的真实项目总结的经验,一些需要注意的地方我在下面都有做说明,希望可以给刚接触大华sdk调用的兄弟一些建议,少走一些弯路!另外大华的文档写的也很详细,都可以对着看!
一、准备工作
1、官网下载大华sdk(注意区分不同平台对应版本,如果项目最终需要部署到LINUX服务器,建议直接下载LINUX版本的sdk,不要折腾windows版的),下载地址:
https://support.dahuatech.com/tools/sdkExploit/24
2、SDK下载好之后,用解压缩工具解压到本地目录,然后用idea打开,可以看到sdk的完整代码结构,主要jar包在libs目录下,技术文档在doc目录下,方法调用demo在src/main/java/com/netsdk/demo下,我们需要调用的示例都可以在这个目录下找到:
3、使用idea的package命令或install命令,直接将整个sdk打成jar包并导入本地maven仓库,有的人会把NetSDKLib下面的代码都复制出来,然后粘贴到自己的项目里,非常不建议这样做,因为要导入的东西实在太多,可能最后自己都放弃了,最好的办法就是直接把sdk打包好扔进maven仓库,需要哪个方法直接调用,添加到maven仓库命令如下(需要根据自己的jar包地址做修改,JNI和大华本身的sdk都要单独打包好放入maven仓库!!):
mvn install:install-file -Dfile=D:\java\apache-maven-3.6.3\linuxJar\netsdk-1.0-demo.jar -DgroupId=com.dahua.netsdk -DartifactId=dahua-netsdk-linux -Dversion=1.0.0 -Dpackaging=jar -DgeneratePom=true
4、在自己的项目pom文件中引入大华sdk相关依赖:
<!-- <!– 这是windows版本的demo–>-->
<!-- <dependency>-->
<!-- <groupId>com.dahua.netsdk</groupId>-->
<!-- <artifactId>dahua-netsdk-windows</artifactId>-->
<!-- <version>1.0.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.dahua.netsdk</groupId>-->
<!-- <artifactId>dahua-netsdk-jni</artifactId>-->
<!-- <version>1.0.0</version>-->
<!-- </dependency>-->
<!-- 这是Linux版本的demo-->
<dependency>
<groupId>com.dahua.netsdk</groupId>
<artifactId>dahua-netsdk-linux</artifactId>
<version>1.0.0</version>
</dependency>
<!--1.1.0是linux的jni包 -->
<dependency>
<groupId>com.dahua.netsdk</groupId>
<artifactId>dahua-netsdk-jni</artifactId>
<version>1.1.0</version>
</dependency>
<!-- 大华摄像头sdk end -->
二、SDK调用思路
1、官方文档好像没有关于人数统计的流程图,临时借用一下智能订阅的流程图,步骤都是差不多的,先初始化-然后登录-向设备订阅事件-回调函数中进行业务逻辑处理等
2、SDK初始化、登录、订阅等功能集成到工具类:
public class SdkUtils {
private final static Logger logger = LoggerFactory.getLogger(DySmsHelper.class);
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);
// 监听服务句柄
public static NetSDKLib.LLong mServerHandler = new NetSDKLib.LLong(0);
private static boolean bInit = false;
private static boolean bLogopen = false;
/**
* 初始化SDK
*
* @param disConnect
* @param haveReConnect
* @return
*/
public static boolean init(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) {
bInit = netsdk.CLIENT_Init(disConnect, null);
if (!bInit) {
logger.info("初始化SDK失败");
return false;
}
//打开日志,可选
NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
File path = new File("./sdklog/");
if (!path.exists()) {
path.mkdir();
}
String logPath = path.getAbsoluteFile().getParent() + "\\sdklog\\" + ToolKits.getDate() + ".log";
setLog.nPrintStrategy = 0;
setLog.bSetFilePath = 1;
System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
System.out.println(logPath);
setLog.bSetPrintStrategy = 1;
bLogopen = netsdk.CLIENT_LogOpen(setLog);
if (!bLogopen) {
System.err.println("Failed to open NetSDK log");
}
// 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,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; // 设置子连接的超时时间
netParam.nGetDevInfoTime = 3000;//获取设备信息超时时间,为0默认1000ms
netsdk.CLIENT_SetNetworkParam(netParam);
return true;
}
/**
* 清除环境
*/
public static void cleanup() {
if (bLogopen) {
netsdk.CLIENT_LogClose();
}
if (bInit) {
netsdk.CLIENT_Cleanup();
}
}
/**
* 登录设备
*/
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 ? false : true;
}
/**
* 登录
*
* @param ip
* @param port
* @param userName
* @param password
* @param deviceIDs
* @return
*/
public static NetSDKLib.LLong login(String ip, int port, String userName, String password, String deviceIDs) {
//入参
NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
Pointer deviceId = ToolKits.GetGBKStringToPointer(deviceIDs);
pstInParam.nPort = port;
pstInParam.szIP = ip.getBytes();
pstInParam.szPassword = password.getBytes();
pstInParam.szUserName = userName.getBytes();
pstInParam.emSpecCap = NetSDKLib.EM_LOGIN_SPAC_CAP_TYPE.EM_LOGIN_SPEC_CAP_SERVER_CONN; // 主动注册登录
pstInParam.pCapParam = deviceId;
// 出参
NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
try {
m_stDeviceInfo.sSerialNumber = deviceIDs.getBytes("GBK");
} catch (Exception e) {
logger.error("转换设备ID异常,deviceID={}", deviceIDs, e);
}
pstOutParam.stuDeviceInfo = m_stDeviceInfo;
m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
long value = m_hLoginHandle.longValue();
if (value == 0) {
logger.error("登录失败,ip={},port={},error={}", ip, port, ToolKits.getErrorCodePrint());
} else {
logger.info("设备登录成功。ip={},port={}", ip, port);
// 登录成功后像物联网平台上报设备状态变化
// reportDeviceStatus(deviceIDs, "online");
}
return m_hLoginHandle;
}
/**
* 登出设备
*/
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;
}
// Attach 订阅 人数统计事件
public static boolean attachVideoStatSummary(int channel, NetSDKLib.fVideoStatSumCallBack fVideoStatSumCallBack) {
NetSDKLib.NET_IN_ATTACH_VIDEOSTAT_SUM inParam = new NetSDKLib.NET_IN_ATTACH_VIDEOSTAT_SUM();
inParam.nChannel = channel;
inParam.cbVideoStatSum = fVideoStatSumCallBack;
NetSDKLib.NET_OUT_ATTACH_VIDEOSTAT_SUM outParam = new NetSDKLib.NET_OUT_ATTACH_VIDEOSTAT_SUM();
NetSDKLib.LLong m_hAttachHandle = LoginModule.netsdk.CLIENT_AttachVideoStatSummary(m_hLoginHandle, inParam, outParam, 5000);
//打开日志,可选
NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
File path = new File("./sdklog/");
if (!path.exists()) {
path.mkdir();
}
String logPath = path.getAbsoluteFile().getParent() + "\\sdklog\\" + ToolKits.getDate() + ".log";
setLog.nPrintStrategy = 0;
setLog.bSetFilePath = 1;
System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
System.out.println(logPath);
setLog.bSetPrintStrategy = 1;
bLogopen = netsdk.CLIENT_LogOpen(setLog);
if (!bLogopen) {
System.err.println("Failed to open NetSDK log");
}
if (m_hAttachHandle.longValue() == 0) {
System.err.printf("Attach Failed!LastError = %s\n", ToolKits.getErrorCodePrint());
return false;
}
System.out.printf("Attach Succeed at Channel %d ! AttachHandle: %d. Wait Device Notify Information\n", channel, m_hAttachHandle.longValue());
return true;
}
}
3、初始化、登录、订阅部分详细代码:
public class ServiceTask extends TimerTask {
private static Logger logger = LoggerFactory.getLogger(ServiceTask.class);
// 设备信息
public static ConcurrentMap<String, DeviceInfo> deviceMap = new ConcurrentHashMap<>();
// 设备断线通知回调
private static DisConnectCallBack disConnectCB = new DisConnectCallBack();
// 网络连接恢复
private static HaveReConnectCallBack haveReConnectCB = new HaveReConnectCallBack();
// 设备是否登录
// public static Map<String, Boolean> deviceLoginMap = new HashMap<>();
public static Map<String, FHumanNumberStatisticCallBack> deviceLoginMap = new HashMap<>();
// 客流量统计订阅回调
// private FHumanNumberStatisticCallBack humanNumberStatisticCB = FHumanNumberStatisticCallBack.getInstance();
private static boolean bInit = false;
private static boolean bLogin = false;
@Resource
private IDeviceInfoService deviceInfoService;
@Override
public void run() {
//从设备表查询设备信息
QueryWrapper<DeviceInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByAsc("channel");
List<DeviceInfo> deviceInfoList = deviceInfoService.list(queryWrapper);
Map<Integer,DeviceInfo> deviceInfoMap = deviceInfoList.stream().collect(Collectors.
toMap(DeviceInfo::getChannel, Function.identity()));//按通道号分组
long currentTimeStamp = System.currentTimeMillis();
//先初始化
if(!bInit){
bInit = SdkUtils.init(disConnectCB, haveReConnectCB);
}
if (!bInit) {
logger.info("初始化SDK失败");
bInit = false;
}else{
if(null!=deviceInfoList&&deviceInfoList.size()>0){
for(DeviceInfo deviceInfo :deviceInfoList){
Boolean reconnectStatus = deviceInfo.getReconnectStatus();
String deviceId = deviceInfo.getDeviceId();
// Boolean logined = deviceLoginMap.getOrDefault(deviceId, false);
FHumanNumberStatisticCallBack logined = deviceLoginMap.get(deviceId);
//当map的连接状态为false时才重新登录
if(null==logined){
//登录摄像头
Boolean isLogin = SdkUtils.login(deviceInfo.getDeviceIp(), deviceInfo.getPort(), deviceInfo.getUserName(), deviceInfo.getPassword());
if(!isLogin){
logger.error("登录摄像头失败,deviceID={},ip={},port={}", deviceInfo.getDeviceId(), deviceInfo.getDeviceIp(), deviceInfo.getPort());
// 登录失败后将摄像头登录状态保存在map中
deviceLoginMap.put(deviceId, null);
deviceInfo.setReconnectStatus(false);
}else{
logger.info("登录摄像头成功,deviceID={},ip={},port={}", deviceInfo.getDeviceId(), deviceInfo.getDeviceIp(), deviceInfo.getPort());
// // 登录成功后将摄像头登录状态保存在map中
// deviceLoginMap.put(deviceId, true);
// deviceInfo.setReconnectStatus(true);
// 登录成功后开启人数统计订阅事件
Integer channel = deviceInfo.getChannel();
FHumanNumberStatisticCallBack humanNumberStatisticCB = new FHumanNumberStatisticCallBack();
// humanNumberStatisticCB.setDeviceId(deviceInfo.getDeviceId());
humanNumberStatisticCB.setDeviceInfoMap(deviceInfoMap);
// 登录成功后将摄像头登录状态保存在map中
deviceLoginMap.put(deviceId, humanNumberStatisticCB);
deviceInfo.setReconnectStatus(true);
Boolean isAttached = SdkUtils.attachVideoStatSummary(channel,humanNumberStatisticCB);
if (!isAttached) {
logger.error("开启人数统计订阅事件失败,deviceInfo={}", deviceInfo);
continue;
}else{
logger.info("开启人数统计订阅事件成功,deviceInfo={}", deviceInfo);
}
}
}
}
//批量更新当前连接状态
deviceInfoService.updateBatchById(deviceInfoList);
}
}
logger.info("结束执行摄像头登录的定时器任务,耗时{}ms",System.currentTimeMillis()-currentTimeStamp);
}
}
4、回调函数内的业务逻辑处理(根据自己业务需要编写):
public class FHumanNumberStatisticCallBack implements NetSDKLib.fVideoStatSumCallBack {
private static IHumanNumberStatisticService humanNumberStatisticService;
// 推送消息记录map
public static Map<String, HumanNumberStatistic> humanNumberStatisticMap = new HashMap<>();
public Map<Integer, DeviceInfo> deviceInfoMap = new HashMap<>();
@Autowired
public void setHumanNumberStatisticService(IHumanNumberStatisticService humanNumberStatisticService) {
FHumanNumberStatisticCallBack.humanNumberStatisticService = humanNumberStatisticService;
}
private static FHumanNumberStatisticCallBack instance = new FHumanNumberStatisticCallBack();
public static FHumanNumberStatisticCallBack getInstance() {
return instance;
}
private EventTaskCommonQueue eventTaskQueue = new EventTaskCommonQueue();
public FHumanNumberStatisticCallBack() {
eventTaskQueue.init();
}
//
// private String deviceId;
@Override
public void invoke(NetSDKLib.LLong lAttachHandle, NetSDKLib.NET_VIDEOSTAT_SUMMARY stVideoState, int dwBufLen, Pointer dwUser) {
SummaryInfo summaryInfo = new SummaryInfo(
stVideoState.nChannelID, stVideoState.stuTime.toStringTime(),
stVideoState.stuEnteredSubtotal.nToday,
stVideoState.stuEnteredSubtotal.nHour,
stVideoState.stuEnteredSubtotal.nTotal,
stVideoState.stuExitedSubtotal.nToday,
stVideoState.stuExitedSubtotal.nHour,
stVideoState.stuExitedSubtotal.nTotal,
stVideoState.nInsidePeopleNum,
stVideoState.emRuleType);
String szRuleName = "";
try {
szRuleName = new String(stVideoState.szRuleName,"gb2312");
}catch(Exception e){
e.printStackTrace();
}
System.out.printf("Channel[%d] GetTime[%s]\n" +
"People In Information[Total[%d] Hour[%d] Today[%d]]\n" +
"People Out Information[Total[%d] Hour[%d] Today[%d]]\n"+
"nInsidePeopleNum[%d]\n"+
"emRuleType[%d]\n"+
"szRuleName[%s]\n"+
"nRetExitManNum[%d]\n",
summaryInfo.nChannelID, summaryInfo.eventTime,
summaryInfo.enteredTotal, summaryInfo.enteredHour, summaryInfo.enteredToday,
summaryInfo.exitedTotal, summaryInfo.exitedHour, summaryInfo.exitedToday
, stVideoState.nInsidePeopleNum
, stVideoState.emRuleType
,szRuleName
, stVideoState.nRetExitManNum);
DeviceInfo deviceInfo = deviceInfoMap.get(summaryInfo.nChannelID);
if(deviceInfo==null){
System.out.println("summaryInfo.nChannelID="+(summaryInfo.nChannelID)+",未匹配到设备");
return;
}
System.out.println("nChannelID="+(summaryInfo.nChannelID)+",deviceInfo="+deviceInfo);
//保存推送数据
HumanNumberStatistic humanNumberStatistic = new HumanNumberStatistic();
humanNumberStatistic.setDeviceId(deviceInfo.getDeviceId());
humanNumberStatistic.setChannel(summaryInfo.nChannelID);
humanNumberStatistic.setEventTime(summaryInfo.eventTime);
humanNumberStatistic.setEnteredTotal(summaryInfo.enteredTotal);
humanNumberStatistic.setEnteredHour(summaryInfo.enteredHour);
humanNumberStatistic.setEnteredToday(summaryInfo.enteredToday);
humanNumberStatistic.setExitedToday(summaryInfo.exitedToday);
humanNumberStatistic.setExitedHour(summaryInfo.exitedHour);
humanNumberStatistic.setExitedTotal(summaryInfo.exitedTotal);
humanNumberStatistic.setNInsidePeopleNum(summaryInfo.nInsidePeopleNum);
humanNumberStatistic.setEmRuleType(summaryInfo.emRuleType);
humanNumberStatistic.setDeviceType(deviceInfo.getDeviceType());
//根据设备id查询当前窗口预设等待时长
humanNumberStatistic.setWaitTimeAverage(humanNumberStatisticService.findWaitTimeAverage(deviceInfo.getDeviceId()));
//双目摄像头,不记录区域内人数统计这条记录
if(deviceInfo.getDeviceType()==2&&(summaryInfo.enteredTotal==0&&summaryInfo.enteredHour==0&&summaryInfo.enteredToday==0
&&summaryInfo.exitedTotal==0&&summaryInfo.exitedHour==0&&summaryInfo.exitedToday==0)){
;
}else{
humanNumberStatisticMap.put(deviceInfo.getDeviceId(),humanNumberStatistic);
}
public Map<Integer, DeviceInfo> getDeviceInfoMap() {
return deviceInfoMap;
}
public void setDeviceInfoMap(Map<Integer, DeviceInfo> deviceInfoMap) {
this.deviceInfoMap = deviceInfoMap;
}
}
三、遇到的一些坑
1、首先就是开头讲到的,如果服务器是linux,千万一开始就别浪费太多时间在windows版本的sdk上,到最后部署的时候会发现linux版本的sdk和windows版还是有些不一样的地方的,windows版本中如果调用的是demo里的方法,有可能在linux的sdk中找不到!
2、sdk的通道号是从0开始的,如果项目对接的是录像机,就要看下通道号是不是和sdk的对的上,应该是要加一才能和sdk对的上的!!
3、demo里给出的字段不全,需要哪些字段可以在NetSDKLib java类里面仔细找,用到哪个自己在summaryInfo里添加!
4、项目完整代码可以移步我的资源里下载,有问题大家一起讨论!