java调用大华SDK实现人流量统计

java调用大华SDK实现人流量统计

  • 前言:
  • 一、准备工作
  • 二、SDK调用思路
  • 三、遇到的一些坑

前言:

    以下内容都是自己最近做的真实项目总结的经验,一些需要注意的地方我在下面都有做说明,希望可以给刚接触大华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下,我们需要调用的示例都可以在这个目录下找到:
java调用大华SDK实现人流量统计_第1张图片
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相关依赖:












		
		
			com.dahua.netsdk
			dahua-netsdk-linux
			1.0.0
		

		
		
			com.dahua.netsdk
			dahua-netsdk-jni
			1.1.0
		
		

二、SDK调用思路

1、官方文档好像没有关于人数统计的流程图,临时借用一下智能订阅的流程图,步骤都是差不多的,先初始化-然后登录-向设备订阅事件-回调函数中进行业务逻辑处理等
java调用大华SDK实现人流量统计_第2张图片
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 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、项目完整代码可以移步我的资源里下载,有问题大家一起讨论!

你可能感兴趣的:(技术,java,java,linux,maven)