海康威视-NVR使用及ISAPI协议透传接入

目录

1、初始化配置

1.1、设置通道默认密码

1.2、添加摄像头

1.3、设置不采集时间段

1.4、抓拍延迟设置

1.5、录像保存时长设置

1.6、人脸库维护

1.7、导入照片

1.8、设置事件

1.8.1、引擎配置

1.8.2、事件设置

1.8.2.1、目标比对

1.8.2.2、设置屏蔽区

1.8.2.3、人脸库

1.9、事件检索

2、ISAPI透传对接

2.1、下载SDK包

2.2、检查是否支持ISAPI

2.3、引入依赖

2.4、启动demo

3、java端接入

3.1、项目结构

3.2、类属性定义

3.3、初始化

3.4、登录

3.5、发起ISAPI调用

3.6、入参对象

3.7、出参对象


背景说明:

使用NVR网络录像机接入监控摄像头,NVR内置人脸库、人脸抓拍、比对功能、事件配置。

使用的设备型号:DS-9632NX-I8R/VPro

1、初始化配置

1.1、设置通道默认密码

1.2、添加摄像头

NVR具有自动广播功能,可以在下方的设备列表中显示所有摄像头。

然后选中添加上上方列表,开启监控。

注意勾选默认密码,否则会引起连接失败问题

1.3、设置不采集时间段

下图灰白区域表示摄像头不采集、不启用事件的事件范围,

选择一个摄像头进行配置

批量复制到其他监控

1.4、抓拍延迟设置

此处设置的开头、结尾延迟5秒,为后续的人物目标识别成功后,截取视频时间段前后五秒,共10秒的视频。默认延迟30s

1.5、录像保存时长设置

1.6、人脸库维护

1.7、导入照片

由于nvr限定jpg、jpeg类型,不支持png,所以需要进行转码

具体参见 Java工具-实现无损png转换jpg格式-CSDN博客

1.8、设置事件

1.8.1、引擎配置

必须要设置目标识别,否则人脸库无法建模,后续人脸目标比对无法进行。

1.8.2、事件设置

选择目标事件,确定必须启用

1.8.2.1、目标比对

选择目标比对,开启,

1.8.2.2、设置屏蔽区

点击右键绘图完毕

1.8.2.3、人脸库

关联人脸库和阈值设置

如果需要提高图片质量,可以设置仰俯角,避免低头、偏头图片

1.9、事件检索

在下图,设置检索条件

2、ISAPI透传对接

2.1、下载SDK包

地址:海康开放平台

下载对应操作系统的开发包

2.2、检查是否支持ISAPI

注意检查默认登录端口8000

选择内部的ISAPI透传的demo

2.3、引入依赖

将对应的库文件引入到项目的lib下

  1. Windows开发时需要

将"库文件"文件夹中的HCNetSDK.dll、HCCore.dll、HCNetSDKCom文件夹、

libssl-1_1-x64.dll、libcrypto-1_1-x64.dll、hlog.dll、hpr.dll、zlib1.dll等文件拷贝到lib文件夹下,

HCNetSDKCom文件夹(包含里面的功能组件dll库文件)需要和HCNetSDK.dll、HCCore.dll一起加载,放在同一个目录下,

且HCNetSDKCom文件夹名不能修改。如果自行开发软件不能正常实现相应功能,而且程序没有指定加载的dll库路径,

请在程序运行的情况下尝试删除HCNetSDK.dll。如果可以删除,说明程序可能调用到系统盘Windows->System32目录下的dll文件,

建议删除或者更新该目录下的相关dll文件;如果不能删除,dll文件右键选择属性确认SDK库版本。

  1. Linux开发时需要将"库文件"文件夹中libhcnetsdk.solibHCCore.so、libcrypto.so.1.1、libssl.so.1.1、libhpr.solibz.so

等文件拷贝到lib文件夹下。HCNetSDKCom文件夹(包含里面的功能组件dll库文件)需要和libhcnetsdk.solibHCCore.so

一起加载,放在同一个目录下,且HCNetSDKCom文件夹名不能修改。如果库文件加载有问题,初始化失败,

也可以尝试将SDK所在路径添加到LD_LIBRARY_PATH环境变量中。

2.4、启动demo

启动类 JavaDemoSTDXMLConfigApp

启动后效果,使用这个demo可以进行api调试。

后续可以参照平台的接口出入参进行对接开发

3、java端接入

3.1、项目结构

win下存放dll文件,需要额外引入 sdk中的examples.jar、jna.jar文件

其中 HIKSDKStructure 类用于解决兼容低版本的Structure调用问题

import com.sun.jna.Structure;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/***
 * 由于 这个问题是因为海康jna.jar比较老,结构体定义没有getFiledOrder,可创建一个类继承 Structure
 * 产生错误:Structure.getFieldOrder() on class com.xxx.sdk.HCNetSDK$NET_DVR_DEVICEINFO_V30 does not provide enough names [0] ([]) to match declared fields [31] ([byAlarmInPortNum,
 * 解决:对 HCNetSDK 接口中的静态类里面,所有继承 Structure 替换为 HIKSDKStructure 即可
 * @author xuancg
 * @date 2024/6/13
 */
public class HIKSDKStructure extends Structure {
    protected List<String> getFieldOrder(){
        List<String> fieldOrderList = new ArrayList<String>();
        for (Class<?> cls = getClass();
             !cls.equals(HIKSDKStructure.class);
             cls = cls.getSuperclass()) {
            Field[] fields = cls.getDeclaredFields();
            int modifiers;
            for (Field field : fields) {
                modifiers = field.getModifiers();
                if (Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
                    continue;
                }
                fieldOrderList.add(field.getName());
            }
        }
        return fieldOrderList;
    }
}

HCNetSDK 类可以通过sdk中查找引入。但需要将所有的Structure 替换为HIKSDKStructure

public class OsSelect {

    public static boolean isLinux() {
        return System.getProperty("os.name").toLowerCase().contains("linux");
    }

    public static boolean isWindows() {
        return System.getProperty("os.name").toLowerCase().contains("windows");
    }
}

3.2、类属性定义

static HCNetSDK hCNetSDK = null;
    static int lUserID;//用户句柄

    private static String rootPath;

    public static final int ISAPI_DATA_LEN = 1024*1024;
    public static final int ISAPI_STATUS_LEN = 4*4096;
    public static final int BYTE_ARRAY_LEN = 1024;

3.3、初始化

public static boolean init(){
        if(null == rootPath){
            rootPath = System.getProperty("user.dir");
             //由于我是多模块下的,此处路径存在问题,实际最好通过外部配置定义
            rootPath += "/my-project";
        }
        log.info("加载lib路径=" + rootPath);
        if (hCNetSDK == null) {
            if (!createSDKInstance()) {
                System.out.println("Load SDK fail");
                return false;
            }
        }
        //linux系统建议调用以下接口加载组件库
        if (OsSelect.isLinux()) {
            HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);
            HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);

            // TODO 需要下载对应linux版本 并替换
            //这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限
            String strPath1 = rootPath + "/libs/libcrypto.so.1.1";
            String strPath2 = rootPath + "/libs/libssl.so.1.1";

            System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());
            ptrByteArray1.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());

            System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());
            ptrByteArray2.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());

            String strPathCom = rootPath + "/libs/";
            HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();
            System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());
            struComPath.write();
            hCNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());
        }

        boolean initSuc = hCNetSDK.NET_DVR_Init();
        if (initSuc != true)
        {
            log.info("初始化失败");
        }
        /**加载日志*/
        return  hCNetSDK.NET_DVR_SetLogToFile(3, "./sdklog", false);
    }


private static boolean createSDKInstance() {
        if (hCNetSDK == null) {
            synchronized (HCNetSDK.class) {
                String strDllPath = "";
                try {
                    if (OsSelect.isWindows())
                        //win系统加载SDK库路径
                        strDllPath = rootPath + "\\libs\\win\\HCNetSDK.dll";

                    else if (OsSelect.isLinux())
                        //Linux系统加载SDK库路径
                        strDllPath = rootPath + "/libs/linux/libhcnetsdk.so";
                    hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);
                } catch (Exception ex) {
                    System.out.println("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

3.4、登录

/**
     * 登录,端口默认8000,成功后会将其他终端登录下线
     * @param sDeviceIP ip地址
     * @param sUsername 登录名
     * @param sPassword 密码
     */
    public static boolean loginByV40(String sDeviceIP,String sUsername,String sPassword){
        //注册之前先注销已注册的用户,预览情况下不可注销
        if (lUserID > -1) {
            //先注销
            hCNetSDK.NET_DVR_Logout(lUserID);
            lUserID = -1;
        }

        //注册
        HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息
        HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息


        m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        System.arraycopy(sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, sDeviceIP.length());

        m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, sUsername.length());

        m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, sPassword.length());

        m_strLoginInfo.wPort = 8000;

        // 此处只能设置0,否则errcode=9
        m_strLoginInfo.byLoginMode = 0;
        //登录模式(不同模式具体含义详见"Remarks"说明):0- SDK私有协议,1- ISAPI协议,2- 自适应(设备支持协议类型未知时使用,一般不建议)
        m_strLoginInfo.byHttps = 0;
        //ISAPI协议登录时是否启用HTTPS(byLoginMode为1时有效):0- 不启用,1- 启用,2- 自适应(设备支持协议类型未知时使用,一般不建议)

        m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是

        m_strLoginInfo.write();
        lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);

        if (lUserID == -1) {
            // 9-从设备接收数据失败。
            log.info("注册失败,错误号:" + hCNetSDK.NET_DVR_GetLastError());
        } else {
            log.info("注册成功,lUserID=" + lUserID);
        }
        return true;
    }


// 登出
public void logout() {
        //注销
        if (lUserID > -1) {
            //先注销
            hCNetSDK.NET_DVR_Logout(lUserID);
            lUserID = -1;
        }
        hCNetSDK.NET_DVR_Cleanup();
    }

3.5、发起ISAPI调用

/**
     * 事件搜索
     * @param req
     * @return
     */
    public EventRecordSearchResp eventSearchApi(EventRecordSearchReq req) {
        HCNetSDK.NET_DVR_XML_CONFIG_INPUT struXMLInput = new HCNetSDK.NET_DVR_XML_CONFIG_INPUT();
        struXMLInput.read();
        struXMLInput.dwSize = struXMLInput.size();
        String strURL = "POST /ISAPI/ContentMgmt/eventRecordSearch?format=json";
        int iURLlen = strURL.length();
        HCNetSDK.BYTE_ARRAY ptrUrl = new HCNetSDK.BYTE_ARRAY(iURLlen+1);
        System.arraycopy(strURL.getBytes(), 0, ptrUrl.byValue, 0, strURL.length());
        ptrUrl.write();
        struXMLInput.lpRequestUrl = ptrUrl.getPointer();
        struXMLInput.dwRequestUrlLen = iURLlen;
        String strInbuffer = JSONUtil.toJsonStr(req);
        int iInBufLen = strInbuffer.length();
        if(iInBufLen==0)
        {
            struXMLInput.lpInBuffer=null;
            struXMLInput.dwInBufferSize=0;
            struXMLInput.write();
        }
        else
        {
            HCNetSDK.BYTE_ARRAY ptrInBuffer = new HCNetSDK.BYTE_ARRAY(iInBufLen+1);
            ptrInBuffer.read();
            ptrInBuffer.byValue = strInbuffer.getBytes();
            ptrInBuffer.write();

            struXMLInput.lpInBuffer = ptrInBuffer.getPointer();
            struXMLInput.dwInBufferSize = iInBufLen;
            struXMLInput.write();

        }
        HCNetSDK.BYTE_ARRAY ptrStatusByte = new HCNetSDK.BYTE_ARRAY(ISAPI_STATUS_LEN);
        ptrStatusByte.read();

        HCNetSDK.BYTE_ARRAY ptrOutByte = new HCNetSDK.BYTE_ARRAY(ISAPI_DATA_LEN);
        ptrOutByte.read();

        HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT struXMLOutput = new HCNetSDK.NET_DVR_XML_CONFIG_OUTPUT();
        struXMLOutput.read();
        struXMLOutput.dwSize = struXMLOutput.size();
        struXMLOutput.lpOutBuffer = ptrOutByte.getPointer();
        struXMLOutput.dwOutBufferSize = ptrOutByte.size();
        struXMLOutput.lpStatusBuffer = ptrStatusByte.getPointer();
        struXMLOutput.dwStatusSize  = ptrStatusByte.size();
        struXMLOutput.write();

        if(!hCNetSDK.NET_DVR_STDXMLConfig(lUserID, struXMLInput, struXMLOutput))
        {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            log.error( "NET_DVR_STDXMLConfig失败,错误号:" + iErr);
            return null;

        }
        else
        {
            struXMLOutput.read();
            ptrOutByte.read();
            ptrStatusByte.read();
            // 输出结果
            String strOutXML = new String(ptrOutByte.byValue).trim();
            // 输出状态
            String strStatus = new String(ptrStatusByte.byValue).trim();
            return JSONUtil.toBean(strOutXML, EventRecordSearchResp.class);
        }
    }

3.6、入参对象

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;

import java.util.UUID;

/***
 * 事件搜索入参
 * @author xuancg
 * @date 2024/6/14
 */
@Data
public class EventRecordSearchReq {

    private EventSearchDescription EventSearchDescription;

    public void setChannels(int...channels){
        this.EventSearchDescription.setChannels(channels);
    }

    public void setEventType(EventType type){
        this.EventSearchDescription.setEventType(type.name());
    }

    public void  setAlarmResult(AlarmResult type){
        this.EventSearchDescription.setAlarmResultAuxEventType(type.name());
    }

    public void setPageNum(int pageNum){
        this.EventSearchDescription.setMaxResults(pageNum);
    }

    /**
     * 默认从1开始
     * @param pageSize
     */
    public void setPageSize(int pageSize){
        int offset = (pageSize - 1) * this.EventSearchDescription.getMaxResults();
        if(offset < 0){
            offset = 0;
        }
        this.EventSearchDescription.setSearchResultPosition(offset);
    }


    @Data
    public class EventSearchDescription{

        public EventSearchDescription() {
            this.searchID = UUID.randomUUID().toString();
        }

        // 随机生成 搜索记录唯一标识, range:[,], desc:用来确认上层客户端是否为同一个(倘若是同一个,则设备记录内存,下次搜索加快速度)
        private String searchID;

        private String eventType;
        private String alarmResultAuxEventType;
        private int[] channels;
        // 每页个数
        private int maxResults = 30;
        // 起始位置相当于offset ,如果第二页则=30
        private int searchResultPosition = 0;

        private String type = "all";
    }

    @Data
    public class timeSpanList {
        // 格式 2024-06-14T23:59:59 08:00
        private String endTime;
        private String startTime;
    }

    /***
     * 检索类型
     */
    @Getter
    @AllArgsConstructor
    public enum EventType{
        alarmResult("目标事件"),
        ;
        private String desc;
    }

    @Getter
    @AllArgsConstructor
    public enum AlarmResult{
        alarmResultSuccess("目标比对成功"),
        ;
        private String desc;
    }

}

3.7、出参对象

import lombok.Data;
import java.util.List;

/***
 * 事件搜索出参
 * 日期格式均为=2024-06-13T11:39:00+08:00  东八区
 * @author xuancg
 * @date 2024/6/14
 */
@Data
public class EventRecordSearchResp {

    private EventSearchResult EventSearchResult;

    @Data
    public class EventSearchResult{

        /**搜索结果数*/
        private int numOfMatches;
        /**是否有更多结果 MORE=是*/
        private String responseStatusStrg;
        /**总结果数*/
        private int totalMatches;

        private List<Targets> Targets;

    }

    @Data
    public class Targets{
        /**抓拍时间*/
        private String alarmEndTime;
        private AlarmResult alarmResult;
        private String alarmResultAuxEventType;
        /**2024-06-13T11:39:00+08:00*/
        private String alarmStartTime;
        private int channel;
        private String eventType;
        private int id;
        private MetadataMatches metadataMatches;
        /**背景图地址http*/
        private String pictureUrl;
        /**人脸范围照地址http*/
        private String smallPictureUrl;
        /**前置后置延迟时间,单位秒 */
        private int postRecordTimeSeconds;
        private int preRecordTimeSeconds;
        /**视频采集开始时间  用于换取采集视频*/
        private String startTime;
        private String endTime;
        /**等同channel*/
        private int triggerChannel;
    }

    @Data
    public class AlarmResult{
        private String FDID;
        /**人脸库名称*/
        private String FDLibName;
        /**人脸库照片地址,http开头*/
        private String FDPicURL;
        private String ageGroup;
        private String gender;
        /**人物姓名*/
        private String name;
        /**相似度,小于1*/
        private float similarity;
    }

    @Data
    public class MetadataMatches{
        private List<AssociatedMetadata> associatedMetadataUrlList;
        private String eventType;
    }

    @Data
    public class AssociatedMetadata {
        private AssociatedMetadataUrl associatedMetadataUrl;
    }

    @Data
    public class AssociatedMetadataUrl {
         private int channelID;
         /**video*/
         private String metadataType;
    }

}

具体完整的代码见资源绑定

相关推荐
火山上的企鹅4 个月前
QT Quick QML 添加海康威视SDK云台控制模块
开发语言·qt·qml·海康威视
小白鼠零号4 个月前
记录|MVS和VM软件使用记录
海康威视·vision master
小白鼠零号4 个月前
记录|Vision Master——logo角度检测案例
海康威视·vision master
xcg3401236 个月前
海康威视-下载的录像视频浏览器播放问题
h264·视频转码·海康威视
Damon小智1 年前
HikvisionCamera开发-萤石云RTMP协议获取视频流
嵌入式硬件·rtmp·摄像头·海康威视·hikvision·萤石云·视频流
四平先森1 年前
海康威视监控相机的SDK与opencv调用(非工业相机)
c++·人工智能·opencv·海康威视
火山上的企鹅1 年前
QGC 中添加海康威视摄像头记录(Qt For Android 使用 JNI 进行JAVA 与 C++ 的通讯)
android·java·qt·海康威视·sdk开发
byxdaz1 年前
NVR添加rtsp流模拟GB28181视频通道
rtsp·nvr·gb28181
黄博大佬1 年前
海康威视二次开发适配安卓电视盒子
sdk·适配·海康威视·安卓盒子·遥控器