java对接海康摄像头

现在制造业很多都是用的海康的摄像头,作为程序员有时候需要对接海康摄像头,实现门禁访问控制,监控预览,录像文件下载等功能。

一、开发环境准备

在海康官网下载SDK开发文档及库文件:
https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10
根据软件部署的平台选择对应的版本,这里以win64版本为例。
在这里插入图片描述

将下载好的库文件复制粘贴加入到java项目中,并改名为lib文件夹。
在这里插入图片描述
注意:demo里的examples.jar和jna.jar也需要放在lib文件下,以方便调试。
在这里插入图片描述
然后在project structure中加入lib库文件中。
在这里插入图片描述
将下载下来的demo中的文件复制粘贴到项目文件中,填入对应的摄像头的账号密码。执行程序,若显示登录成功,则表示调试成功。
在这里插入图片描述

二、实现java调用设备接口

打开下载的《设备网络SDK使用手册.chm》作为参考,在该手册中java如何对接海康摄像头有详细说明。在这里插入图片描述

由于设备网络SDK是封装的动态链接库(Windows的dll或者Linux的so),各种开发语言对接SDK,都是通过加载动态库链接,调用动态库中的接口实现功能模块对接,因此,设备网络SDK的对接不区分开发语言,而且对接的流程和对应的接口都是通用的,各种语言调用动态库的方式有所不同。

java语言是通过JNA的方式调用动态链接库中的接口,实现在java语言中调用C/C++语言封装的接口

因此,java的类文件不需要编写任何业务代码实现某接口来调用设备,而是声明一个接口就能调用设备的功能了。

这只需要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射,而不需要编写任何Native/JNI代码,大大降低了Java调用动态链接库的开发难度。

JNA调用C/C++的过程大致如下:
在这里插入图片描述

(一)加载动态链接库

通过java调用海康官方提供的设备功能,首先需要自定义一个接口加载dll文件,比如demo中是声明HCNetSDK的接口,该接口继承Library 或 StdCallLibrary。

默认的是继承Library ,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary。接口内部需要一个公共静态常量:INSTANCE,通过这个常量,就可以获得这个接口的实例,从而使用接口的方法,也就是调用外部dll/so的函数。

INSTANCE常量通过 Native.loadLibrary() API函数获得,(新版本的jna中,常量是通过Native.load()函数获取的)该函数有2个参数:

// SDK接口说明,HCNetSDK.dll
public interface HCNetSDK extends StdCallLibrary {
	HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\HCNetSDK.dll", HCNetSDK.class);
	// 动态库中结构体、接口描述
}

这里是采用的是绝对路径,为防止项目工程路径的变化,可以改为获取动态路径。

public class XXDemo {
	static HCNetSDK hCNetSDK = null;

    //动态库加载,根据软件所属操作系统的工程文件目录动态获取库文件路径
	private static boolean createSDKInstance() {
	    if (hCNetSDK == null) {
	        synchronized (HCNetSDK.class) {
	            String strDllPath = "";
	            try {
	                if (osSelect.isWindows())
	                    //win系统加载库路径
	                    strDllPath = System.getProperty("user.dir") + "\\lib\\HCNetSDK.dll";
	                else if (osSelect.isLinux())
	                    //Linux系统加载库路径
	                    strDllPath = System.getProperty("user.dir") + "/lib/libhcnetsdk.so";
	                hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);
	            } catch (Exception ex) {
	                System.out.println("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
	                return false;
	            }
	        }
	    }
	    return true;
	}
	
	public static void main(String[] args) throws InterruptedException {
		//调用函数之前先加载动态链接库
		if (hCNetSDK == null) {
		    if (!createSDKInstance()) {
		        System.out.println("Load SDK fail");
		        return;
		    }
		}
	}
}

类似的,别的dll库文件的接口也是如此进行声明。

// 播放库函数声明,PlayCtrl.dll
public interface PlayCtrl extends StdCallLibrary{
    PlayCtrl INSTANCE = (PlayCtrl) Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\PlayCtrl.dll", PlayCtrl.class);
    // 播放库中结构体,接口描述
}

(二)结构体、接口重定义

dll和so是C/C++语言函数的集合和容器,与Java中的接口概念吻合,所以JNA把dll文件和so文件看成一个个接口。

在JNA中定义一个接口就是相当于了定义一个DLL/SO文件的描述文件,该接口代表了动态链接库中发布的所有函数,对于程序不需要的函数,可以不在接口中声明

例如,官方demo中的HCNetSDK的java接口声明了HCNetSDK.dll/so文件很多的接口。在实际开发中可以不需要声明这么多接口,只取某些需要的接口重新进行分组。

若在《设备网络SDK使用手册》查找功能时发现某些接口在demo中没有,则可以在HCNetSDK.h头文件搜索对应的接口,然后在java中声明该接口的定义。
在这里插入图片描述

1.类型映射

接口中使用的函数必须与链接库中的函数原型保持一致,因为C/C++的类型与Java的类型是不一样的,动态库中的C/C++的数据类型必须转换成java对应类型,这就是类型映射(Type Mappings)。

默认的类型映射可以参考JNA官网的类型映射表。(国内镜像链接 https://gitee.com/mirrors/jna/)
在这里插入图片描述

2.结构体和类的转换

Java中没有结构体(struct)这种数据类型,JNA为我们提供了Structure这个类,只要继承该类,就可实现java结构体,相当于转换成java中的类。

例如,NET_DVR_USER_LOGIN_INFO结构体需要在HCNetSDK接口中进行重定义,转换方式如下:

//C++中NET_DVR_USER_LOGIN_INFO结构体定义
typedef struct{
    char sDeviceAddress[NET_DVR_DEV_ADDRESS_MAX_LEN];
    BYTE byUseTransport;  // 是否启用能力集透传,0--不启用透传,默认,1--启用透传
    WORD wPort;
    char sUserName[NET_DVR_LOGIN_USERNAME_MAX_LEN];
    char sPassword[NET_DVR_LOGIN_PASSWD_MAX_LEN];
    fLoginResultCallBack cbLoginResult;
    void *pUser;
    BOOL bUseAsynLogin;
    BYTE byProxyType;   // 0:不使用代理,1:使用标准代理,2:使用EHome代理
    BYTE byUseUTCTime;  // 0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换
    BYTE byLoginMode;   // 0-Private 1-ISAPI 2-自适应
    BYTE byHttps;       // 0-不适用tls,1-使用tls 2-自适应
    LONG iProxyID;      // 代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值
    BYTE byVerifyMode;  // 认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效;
    BYTE byRes3[119];
}NET_DVR_USER_LOGIN_INFO,*LPNET_DVR_USER_LOGIN_INFO;

// 宏定义
#define NET_DVR_DEV_ADDRESS_MAX_LEN 129
#define NET_DVR_LOGIN_USERNAME_MAX_LEN 64
#define NET_DVR_LOGIN_PASSWD_MAX_LEN 64

注意转换之后,java类中的属性都为public,这意味着可以直接进行属性的修改和读取,而不需要进行get和set方法的声明和操作。

// SDK接口说明,HCNetSDK.dll
public interface HCNetSDK extends StdCallLibrary {

    HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\HCNetSDK.dll", HCNetSDK.class);

    // 动态库中结构体、接口描述
    public static class NET_DVR_USER_LOGIN_INFO extends Structure{
        public byte[] sDeviceAddress = new byte[NET_DVR_DEV_ADDRESS_MAX_LEN];
        public byte byUseTransport;
        public short wPort;
        public byte[] sUserName = new byte[NET_DVR_LOGIN_USERNAME_MAX_LEN];
        public byte[] sPassword = new byte[NET_DVR_LOGIN_PASSWD_MAX_LEN];
        public FLoginResultCallback cbLoginResult;
        public Pointer pUser;
        public boolean bUseAsynLogin;
        public byte byProxyType;   // 0:不使用代理,1:使用标准代理,2:使用EHome代理
        public byte byUseUTCTime;  // 0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换
        public byte byLoginMode;   // 0-Private 1-ISAPI 2-自适应
        public byte byHttps;       // 0-不适用tls,1-使用tls 2-自适应
        public int iProxyID;    // 代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值
        public byte byVerifyMode;  // 认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效;
        public byte[] byRes2 = new byte[119];

        // 结构体中重写getFieldOrder方法,FieldOrder顺序要和结构体中定义的顺序保持一致
        @Override
        protected List getFieldOrder(){
            return  Arrays.asList("sDeviceAddress","byUseTransport","wPort","sUserName","sPassword",                                                               "cbLoginResult","pUser","bUseAsynLogin","byProxyType","byUseUTCTime",
                                  "byLoginMode","byHttps","iProxyID","byVerifyMode","byRes2");
        } 

    }

    // 常量(宏)定义
    public static final int NET_DVR_DEV_ADDRESS_MAX_LEN = 129;
    public static final int NET_DVR_LOGIN_USERNAME_MAX_LEN = 64;
    public static final int NET_DVR_LOGIN_PASSWD_MAX_LEN = 64;   
}

3.接口转换

在HCNetSDK接口中声明的方法要和开发包中HCNetSDK.h的头文件中声明的函数对应上,其中方法名、参数列表、返回值都要和HCNetSDK.h中的函数对应,HCNetSDK.h头文件的函数转换到java中声明,转换方式如下所示:

/******************************** SDK接口函数声明 *********************************/

// 初始化SDK,调用其他SDK函数的前提
NET_DVR_API BOOL __stdcall NET_DVR_Init();  

// 启用日志文件写入接口
NET_DVR_API BOOL __stdcall NET_DVR_SetLogToFile(DWORD nLogLevel ,char* strLogDir, BOOL bAutoDel); 

// 返回最后操作的错误码
NET_DVR_API DWORD __stdcall NET_DVR_GetLastError(); 

// 释放SDK资源,在程序结束之前调用
NET_DVR_API BOOL __stdcall NET_DVR_Cleanup(); 

// 登录接口
NET_DVR_API LONG __stdcall NET_DVR_Login_V40(
    LPNET_DVR_USER_LOGIN_INFO pLoginInfo,
    LPNET_DVR_DEVICEINFO_V40 lpDeviceInfo ); 

// 用户注销
NET_DVR_API BOOL __stdcall NET_DVR_Logout(LONG lUserID); 

// 回调函数声明,登录状态回调函数
typedef void (CALLBACK *fLoginResultCallBack) (
    LONG lUserID, DWORD dwResult,
    LPNET_DVR_DEVICEINFO_V30 lpDeviceInfo , 
    void* pUser );  

转换到java中时,注意类型转换

// SDK接口说明,HCNetSDK.dll
public interface HCNetSDK extends StdCallLibrary {

HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\HCNetSDK.dll", HCNetSDK.class);

    /*** API函数声明 ***/

    // 初始化SDK,调用其他SDK函数的前提
    boolean NET_DVR_Init();  

    // 启用日志文件写入接口
    boolean NET_DVR_SetLogToFile(int bLogEnable , String  strLogDir, boolean bAutoDel); 

    // 返回最后操作的错误码
    int NET_DVR_GetLastError();  

    // 释放SDK资源,在程序结束之前调用
    boolean NET_DVR_Cleanup();

    // 登录接口
    int NET_DVR_Login_V40(NET_DVR_USER_LOGIN_INFO pLoginInfo, NET_DVR_DEVICEINFO_V40 lpDeviceInfo); 

    // 用户注销
    boolean NET_DVR_Logout(int lUserID); 

    // 回调函数申明
    public static interface FLoginResultCallback extends StdCallCallback{
	    // 登录状态回调函数
	    public int invoke(int lUserID,int dwResult,NET_DVR_DEVICEINFO_V30 lpDeviceinfo,Pointer pUser);
    } 
}

4.方法调用

经过上述的操作,JNA工程已经创建完成,结构体和函数也在HCNetSDK接口类中进行了转换,后续就可以在主类中实现调用。
在这里插入图片描述
以下为官方demo中的示例,以实现用户注册功能模块为例,解释了接口调用的流程。

public class jna_test {
    // 接口的实例,通过接口实例调用外部dll/so的函数
    static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE; 

    // 用户登录返回句柄
    static int lUserID;  
    int iErr = 0;

    public static void main(String[] args) throws InterruptedException {
        jna_test test01 = new jna_test();
        // 初始化
        boolean initSuc = hCNetSDK.NET_DVR_Init();
        if (initSuc != true) {
            System.out.println("初始化失败");
        }
        // 打印SDK日志
        hCNetSDK.NET_DVR_SetLogToFile(3, ".\\SDKLog\\", false);
        // 用户登陆操作
        test01.Login_V40("192.168.1.64",(short)8000,"admin","test12345");
        /*
        *实现SDK中其余功能模快
        */
        Thread.sleep(5000);
        //用户注销,释放SDK
        test01.Logout();    
    }
    /**
     *
     * @param m_sDeviceIP 设备ip地址
     * @param wPort       端口号,设备网络SDK登录默认端口8000
     * @param m_sUsername 用户名
     * @param m_sPassword 密码
     */
    public void Login_V40(String m_sDeviceIP,short wPort,String m_sUsername,String m_sPassword) {
        /* 注册 */
        // 设备登录信息
        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(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());
        m_strLoginInfo.wPort =wPort ;
        m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());
        m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length());
        // 是否异步登录:false- 否,true- 是
        m_strLoginInfo.bUseAsynLogin = false; 
        // write()调用后数据才写入到内存中
        m_strLoginInfo.write();  

        lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
        if (lUserID == -1) {
            System.out.println("登录失败,错误码为" + hCNetSDK.NET_DVR_GetLastError());
            return;
        } else {
            System.out.println("登录成功!");

            // read()后,结构体中才有对应的数据 
            m_strDeviceInfo.read();  
            return;
        }
    }
    //设备注销 SDK释放
    public void Logout() {
        if (lUserID>=0)
        {
            if (hCNetSDK.NET_DVR_Logout(lUserID) == false) {
                System.out.println("注销失败,错误码为" + hCNetSDK.NET_DVR_GetLastError());
            }
            System.out.println("注销成功");
            hCNetSDK.NET_DVR_Cleanup();
            return;
        }
        else{
            System.out.println("设备未登录");
            hCNetSDK.NET_DVR_Cleanup();
            return;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/777391.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于如何做好淘汰 IT 资产数据安全销毁工作的思考 文件销毁 硬盘销毁 数据销毁 物料销毁 文件粉碎

在当今数字化时代,企业的 IT 资产不断更新换代,淘汰的 IT 资产中往往存储着大量的敏感数据。如何确保这些数据在资产淘汰过程中被安全销毁,成为了企业面临的重要挑战。以下是对如何做好淘汰 IT 资产数据安全销毁工作的一些思考。 一、明确数…

WACV2023论文速览域迁移Domain相关

Paper1 CellTranspose: Few-Shot Domain Adaptation for Cellular Instance Segmentation 摘要原文: Automated cellular instance segmentation is a process utilized for accelerating biological research for the past two decades, and recent advancements have produc…

控制下属绝不是拍桌子、甩脸子、摆架子,一流领导都用这3招!

控制下属绝不是拍桌子、甩脸子、摆架子,一流领导都用这3招! 第一招:规矩严 国有国法,家有家规,公司也不例外。 下属能不能和领导齐心,愿不愿意共同发力,全看管理者如何操控。 毫无疑问的是&a…

Leetcode - 周赛403

目录 一,3200. 三角形的最大高度 二,3195. 包含所有 1 的最小矩形面积 I 三,3196. 最大化子数组的总成本 四,3197. 包含所有 1 的最小矩形面积 II 一,3200. 三角形的最大高度 本题是一道模拟题,可以先排…

【不容错过】可灵AI重磅更新:画质升级,运镜控制,首尾帧自定义,还有30万创作激励奖金!

还记得最近在各大平台肆虐的老照片变成视频吗,就是用快手的可灵AI做的,今天可灵又迎来了一次重大更新。 「电脑端上线了」 之前一直用其他工具生的图片还需要保存到手机上,再用可灵来生成视频,很多人都能感受到手机操作不太方便&…

七人互助拼团模式:共创共赢的电商新篇章

在当今电商行业的繁荣浪潮中,七人互助拼团模式犹如一股清流,凭借其独特的激励机制与深厚的互助合作文化,赢得了消费者与商家的广泛赞誉。这一模式不仅重新定义了团购体验,更在无形中强化了社群间的联系与协作,共同绘制…

提升用户体验之requestAnimationFrame实现前端动画

1)requestAnimationFrame是什么? 1.MDN官方解释 2.解析这段话: 1、那么浏览器重绘是指什么呢? ——大多数电脑的显示器刷新频率是60Hz,1000ms/6016.66666667ms的时间刷新一次 2、重绘之前调用指定的回调函数更新动画? ——requ…

多特征线性回归

目录 一、多特征符号意义说明:二、多特征模型表示:三、Numpy向量表示、内积计算:1.向量表示:2.内积计算: 四、多元线性回归梯度下降算法: 一、多特征符号意义说明: x下标j:表示第j个…

Docker:一、安装与卸载、配置阿里云加速器(Ubuntu)

目录 🍁安装docker🌲1、环境准备🌲2、安装docker Engine🧊1、卸载旧版、任何冲突的包🧊2、使用存储库安装🧊3、安装 Docker 包。🧊4、查询是否安装成功🧊5、运行hello-world镜像&…

医疗器械FDA | FDA如何对医疗器械网络安全认证进行审查?

FDA医械网络安全文件出具​https://link.zhihu.com/?targethttps%3A//www.wanyun.cn/Support%3Fshare%3D24315_ea8a0e47-b38d-4cd6-8ed1-9e7711a8ad5e FDA对医疗器械的网络安全认证进行审查时,主要关注以下几个方面,以确保医疗器械在网络环境中的安全性…

MAX98357、MAX98357A、MAX98357B小巧、低成本、PCM D类IIS放大器,具有AB类性能中文说明规格书

前言: MAX98357A支持标准I2S数据,MAX98357B支持左对齐数字音频数据。两个版本均支持8通道TDM音频数据。 IIS数字功放MAX98357开发板/评估系统 MAX98357 WLP-9(1.347x1.437mm)封装的外观和丝印AKM MAX98357 TQFN-16-EP(3x3mm)封装的外观和丝印AKK 引脚说…

Rust: polars行遍历,从dataframe到struct及Bar设计比较

pandas提供了iterrows()、itertuples()、apply等行遍历的方式,还是比较方便的。 polars的列操作功能非常强大,这个在其官网上有详细的介绍。由于polars底层的arrow是列存储模式,行操作效率低下,官方也不推荐以行方式进行数据操作。…

谷粒商城 - 树形菜单递归流查询、三级分类数据查询性能优化、Jmter 性能压测

目录 树形分类菜单(递归查询,强扩展) 1)需求 2)数据库表设计 3)实现 4)关于 asSequence 优化 性能压测 1)Jmeter 安装使用说明 2)中间件对性能的影响 三级分类数…

昇思25天学习打卡营第5天|GAN图像生成

文章目录 昇思MindSpore应用实践基于MindSpore的生成对抗网络图像生成1、生成对抗网络简介零和博弈 vs 极大极小博弈GAN的生成对抗损失 2、基于MindSpore的 Vanilla GAN3、基于MindSpore的手写数字图像生成导入数据数据可视化模型训练 Reference 昇思MindSpore应用实践 本系列…

01 企业网站架构部署于优化之Web基础与HTTP协议

目录 1.1 Web基础 1.1.1 域名和DNS 1. 域名的概念 2. Hosts文件 3. DNS 4. 域名注册 1.1.2 网页与HTML 1. 网页概述 2. HTML概述 3. HTML基本标签 4. 网站和主页 5. Web1.0与Web2.0 1.1.3 静态网页与动态网页 1. 静态网页 2. 动态网页 3. 动态网页语言 1.2 HTTP协议 1…

如何快速开展每日待办工作 待办任务高效管理

每天,我们都需要处理大量的待办工作,如何高效有序地开展这些工作成为了我们必须要面对的问题。仅仅依靠个人的记忆和脑力去管理这些繁杂的事务,显然是一项艰巨的挑战。在这个时候,如果能有一款实用的待办工具来辅助我们&#xff0…

7年跨境业务资深从业者的代理IP使用经验分享

作为一个拥有7年跨境业务经验的资深从业者,今天大家分享一下在使用各种代理IP服务中的宝贵经验。无论你是新手还是老手,这篇文章都能为你带来一些新的启发和实用技巧。 在我刚开始跨境业务的那几年,最大的挑战之一就是如何跨境访问&#xff0…

ORB 特征点提取

FAST关键点 选取像素p,假设它的亮度为Ip; . 设置一个阈值T(比如Ip的20%); 以像素p为中心,选取半径为3的圆上的16个像素点; 假如选取的圆上,有连续的N个点的亮度大于IpT或小于…

CSS实现图片裁剪居中(只截取剪裁图片中间部分,图片不变形)

1.第一种方式:(直接给图片设置:object-fit:cover;) .imgbox{width: 100%;height:200px;overflow: hidden;position: relative;img{width: 100%;height: 100%; //图片要设置高度display: block;position: absolute;left: 0;right…

【Python基础篇】你了解python中运算符吗

文章目录 1. 算数运算符1.1 //整除1.2 %取模1.3 **幂 2. 赋值运算符3. 位运算符3.1 &&#xff08;按位与&#xff09;3.2 |&#xff08;按位或&#xff09;3.3 ^&#xff08;按位异或&#xff09;3.4 ~&#xff08;按位取反&#xff09;3.5 <<&#xff08;左移&#…