java对接海康摄像头的完整步骤记录

2025-06-09 16:50

本文主要是介绍java对接海康摄像头的完整步骤记录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《java对接海康摄像头的完整步骤记录》在Java中调用海康威视摄像头通常需要使用海康威视提供的SDK,下面这篇文章主要给大家介绍了关于java对接海康摄像头的完整步骤,文中通过代码介绍的非常详细,需...

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

一、开发环境准备

在海康官网下载SDK开发文档及库文件:

https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10

根据软件部署的平台选择对应的版本,这里以win64版本为例。

java对接海康摄像头的完整步骤记录

将下载好的库文件复制粘贴加入到java项目中,并改名为lib文件夹。

java对接海康摄像头的完整步骤记录

注意:demo里的examples.jar和jna.jar也需要放在lib文件下,以方便调试。

java对接海康摄像头的完整步骤记录

然后在project structure中加入lib库文件中。

java对接海康摄像头的完整步骤记录

将下载下来的demo中的文件复制粘贴到项目文件中,填入对应的摄像头的账号密码。执行程序,若显示登录成功,则表示调试成功。

java对接海康摄像头的完整步骤记录

二、实现java调用设备接口

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

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对接海康摄像头的完整步骤记录

(一)加载动态链接库

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

默认的是继承Library ,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary。接口内部需要一个公共静态常量:INSTANCE,通过这个常量,就可以获得这个接口的实例,从而使用http://www.chinasem.cn接口的方法,也就是调用外部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库oGCMirYSi文件的接口也是如此进行声明。

// 播放库函数声明,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中声明该接口的定义。

java对接海康摄像头的完整步骤记录

1.类型映射

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

默认的类型映射可以参考JNA官网的类型映射表。(国内镜像链接 https://gitee.com/mirrors/jna/)

java对接海康摄像头的完整步骤记录

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 = (HCNetoGCMirYSiSDK) 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接口类中进行了转换,后续就可以在主类中实现调用。

java对接海康摄像头的完整步骤记录

以下为官方demo中的示例,以实现用户注册功能模块为例,解释了接口调用的流程。

puboGCMirYSilic 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","test12345http://www.chinasem.cn");
        /*
        *实现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;
        }
    }
}

我自己也写了个DEMO,放在gitee仓库了。可以提供参考:https://gitee.com/ZachLong/java-hik-camera

总结

到此这篇关于java对接海康摄像头的文章就介绍到这了,更多相关java对接海康摄像头内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于java对接海康摄像头的完整步骤记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154975

相关文章

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版