找回密码
 会员注册
查看: 40|回复: 0

java对接海康摄像头

[复制链接]

7

主题

0

回帖

22

积分

新手上路

积分
22
发表于 2024-9-3 18:54:22 | 显示全部楼层 |阅读模式
现在制造业很多都是用的海康的摄像头,作为程序员有时候需要对接海康摄像头,实现门禁访问控制,监控预览,录像文件下载等功能。一、开发环境准备在海康官网下载SDK开发文档及库文件:https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10根据软件部署的平台选择对应的版本,这里以win64版本为例。将下载好的库文件复制粘贴加入到java项目中,并改名为lib文件夹。注意:demo里的examples.jar和jna.jar也需要放在lib文件下,以方便调试。然后在projectstructure中加入lib库文件中。将下载下来的demo中的文件复制粘贴到项目文件中,填入对应的摄像头的账号密码。执行程序,若显示登录成功,则表示调试成功。二、实现java调用设备接口打开下载的《设备网络SDK使用手册.chm》作为参考,在该手册中java如何对接海康摄像头有详细说明。由于设备网络SDK是封装的动态链接库(Windows的dll或者Linux的so),各种开发语言对接SDK,都是通过加载动态库链接,调用动态库中的接口实现功能模块对接,因此,设备网络SDK的对接不区分开发语言,而且对接的流程和对应的接口都是通用的,各种语言调用动态库的方式有所不同。java语言是通过JNA的方式调用动态链接库中的接口,实现在java语言中调用C/C++语言封装的接口。因此,java的类文件不需要编写任何业务代码实现某接口来调用设备,而是声明一个接口就能调用设备的功能了。这只需要在一个java接口中描述目标nativelibrary的函数与结构,JNA将自动实现Java接口到nativefunction的映射,而不需要编写任何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.dllpublicinterfaceHCNetSDKextendsStdCallLibrary{ HCNetSDKINSTANCE=(HCNetSDK)Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\HCNetSDK.dll",HCNetSDK.class); //动态库中结构体、接口描述}12345这里是采用的是绝对路径,为防止项目工程路径的变化,可以改为获取动态路径。publicclassXXDemo{ staticHCNetSDKhCNetSDK=null;//动态库加载,根据软件所属操作系统的工程文件目录动态获取库文件路径 privatestaticbooleancreateSDKInstance(){ if(hCNetSDK==null){ synchronized(HCNetSDK.class){ StringstrDllPath=""; try{ if(osSelect.isWindows()) //win系统加载库路径 strDllPath=System.getProperty("user.dir")+"\\lib\\HCNetSDK.dll"; elseif(osSelect.isLinux()) //Linux系统加载库路径 strDllPath=System.getProperty("user.dir")+"/lib/libhcnetsdk.so"; hCNetSDK=(HCNetSDK)Native.loadLibrary(strDllPath,HCNetSDK.class); }catch(Exceptionex){ System.out.println("loadLibrary:"+strDllPath+"Error:"+ex.getMessage()); returnfalse; } } } returntrue; } publicstaticvoidmain(String[]args)throwsInterruptedException{ //调用函数之前先加载动态链接库 if(hCNetSDK==null){ if(!createSDKInstance()){ System.out.println("LoadSDKfail"); return; } } }}1234567891011121314151617181920212223242526272829303132333435类似的,别的dll库文件的接口也是如此进行声明。//播放库函数声明,PlayCtrl.dllpublicinterfacePlayCtrlextendsStdCallLibrary{PlayCtrlINSTANCE=(PlayCtrl)Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\PlayCtrl.dll",PlayCtrl.class);//播放库中结构体,接口描述}123456(二)结构体、接口重定义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对应类型,这就是类型映射(TypeMappings)。默认的类型映射可以参考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结构体定义typedefstruct{charsDeviceAddress[NET_DVR_DEV_ADDRESS_MAX_LEN];BYTEbyUseTransport;//是否启用能力集透传,0--不启用透传,默认,1--启用透传WORDwPort;charsUserName[NET_DVR_LOGIN_USERNAME_MAX_LEN];charsPassword[NET_DVR_LOGIN_PASSWD_MAX_LEN];fLoginResultCallBackcbLoginResult;void*pUser;BOOLbUseAsynLogin;BYTEbyProxyType;//0:不使用代理,1:使用标准代理,2:使用EHome代理BYTEbyUseUTCTime;//0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换BYTEbyLoginMode;//0-Private1-ISAPI2-自适应BYTEbyHttps;//0-不适用tls,1-使用tls2-自适应LONGiProxyID;//代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值BYTEbyVerifyMode;//认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效;BYTEbyRes3[119];}NET_DVR_USER_LOGIN_INFO,*LPNET_DVR_USER_LOGIN_INFO;//宏定义#defineNET_DVR_DEV_ADDRESS_MAX_LEN129#defineNET_DVR_LOGIN_USERNAME_MAX_LEN64#defineNET_DVR_LOGIN_PASSWD_MAX_LEN641234567891011121314151617181920212223注意转换之后,java类中的属性都为public,这意味着可以直接进行属性的修改和读取,而不需要进行get和set方法的声明和操作。//SDK接口说明,HCNetSDK.dllpublicinterfaceHCNetSDKextendsStdCallLibrary{HCNetSDKINSTANCE=(HCNetSDK)Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\HCNetSDK.dll",HCNetSDK.class);//动态库中结构体、接口描述publicstaticclassNET_DVR_USER_LOGIN_INFOextendsStructure{publicbyte[]sDeviceAddress=newbyte[NET_DVR_DEV_ADDRESS_MAX_LEN];publicbytebyUseTransport;publicshortwPort;publicbyte[]sUserName=newbyte[NET_DVR_LOGIN_USERNAME_MAX_LEN];publicbyte[]sPassword=newbyte[NET_DVR_LOGIN_PASSWD_MAX_LEN];publicFLoginResultCallbackcbLoginResult;publicPointerpUser;publicbooleanbUseAsynLogin;publicbytebyProxyType;//0:不使用代理,1:使用标准代理,2:使用EHome代理publicbytebyUseUTCTime;//0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换publicbytebyLoginMode;//0-Private1-ISAPI2-自适应publicbytebyHttps;//0-不适用tls,1-使用tls2-自适应publicintiProxyID;//代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值publicbytebyVerifyMode;//认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效;publicbyte[]byRes2=newbyte[119];//结构体中重写getFieldOrder方法,FieldOrder顺序要和结构体中定义的顺序保持一致@OverrideprotectedListgetFieldOrder(){returnArrays.asList("sDeviceAddress","byUseTransport","wPort","sUserName","sPassword","cbLoginResult","pUser","bUseAsynLogin","byProxyType","byUseUTCTime","byLoginMode","byHttps","iProxyID","byVerifyMode","byRes2");}}//常量(宏)定义publicstaticfinalintNET_DVR_DEV_ADDRESS_MAX_LEN=129;publicstaticfinalintNET_DVR_LOGIN_USERNAME_MAX_LEN=64;publicstaticfinalintNET_DVR_LOGIN_PASSWD_MAX_LEN=64;}123456789101112131415161718192021222324252627282930313233343536373.接口转换在HCNetSDK接口中声明的方法要和开发包中HCNetSDK.h的头文件中声明的函数对应上,其中方法名、参数列表、返回值都要和HCNetSDK.h中的函数对应,HCNetSDK.h头文件的函数转换到java中声明,转换方式如下所示:/********************************SDK接口函数声明*********************************///初始化SDK,调用其他SDK函数的前提NET_DVR_APIBOOL__stdcallNET_DVR_Init();//启用日志文件写入接口NET_DVR_APIBOOL__stdcallNET_DVR_SetLogToFile(DWORDnLogLevel,char*strLogDir,BOOLbAutoDel);//返回最后操作的错误码NET_DVR_APIDWORD__stdcallNET_DVR_GetLastError();//释放SDK资源,在程序结束之前调用NET_DVR_APIBOOL__stdcallNET_DVR_Cleanup();//登录接口NET_DVR_APILONG__stdcallNET_DVR_Login_V40(LPNET_DVR_USER_LOGIN_INFOpLoginInfo,LPNET_DVR_DEVICEINFO_V40lpDeviceInfo);//用户注销NET_DVR_APIBOOL__stdcallNET_DVR_Logout(LONGlUserID);//回调函数声明,登录状态回调函数typedefvoid(CALLBACK*fLoginResultCallBack)(LONGlUserID,DWORDdwResult,LPNET_DVR_DEVICEINFO_V30lpDeviceInfo,void*pUser);123456789101112131415161718192021222324252627转换到java中时,注意类型转换//SDK接口说明,HCNetSDK.dllpublicinterfaceHCNetSDKextendsStdCallLibrary{HCNetSDKINSTANCE=(HCNetSDK)Native.loadLibrary("E:\\DEMO_TEST\\JAVA_Demo\\JNA_TEST\\lib\\HCNetSDK.dll",HCNetSDK.class);/***API函数声明***///初始化SDK,调用其他SDK函数的前提booleanNET_DVR_Init();//启用日志文件写入接口booleanNET_DVR_SetLogToFile(intbLogEnable,StringstrLogDir,booleanbAutoDel);//返回最后操作的错误码intNET_DVR_GetLastError();//释放SDK资源,在程序结束之前调用booleanNET_DVR_Cleanup();//登录接口intNET_DVR_Login_V40(NET_DVR_USER_LOGIN_INFOpLoginInfo,NET_DVR_DEVICEINFO_V40lpDeviceInfo);//用户注销booleanNET_DVR_Logout(intlUserID);//回调函数申明publicstaticinterfaceFLoginResultCallbackextendsStdCallCallback{ //登录状态回调函数 publicintinvoke(intlUserID,intdwResult,NET_DVR_DEVICEINFO_V30lpDeviceinfo,PointerpUser);}}123456789101112131415161718192021222324252627282930314.方法调用经过上述的操作,JNA工程已经创建完成,结构体和函数也在HCNetSDK接口类中进行了转换,后续就可以在主类中实现调用。以下为官方demo中的示例,以实现用户注册功能模块为例,解释了接口调用的流程。publicclassjna_test{//接口的实例,通过接口实例调用外部dll/so的函数staticHCNetSDKhCNetSDK=HCNetSDK.INSTANCE;//用户登录返回句柄staticintlUserID;intiErr=0;publicstaticvoidmain(String[]args)throwsInterruptedException{jna_testtest01=newjna_test();//初始化booleaninitSuc=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);//用户注销,释放SDKtest01.Logout();}/****@paramm_sDeviceIP设备ip地址*@paramwPort端口号,设备网络SDK登录默认端口8000*@paramm_sUsername用户名*@paramm_sPassword密码*/publicvoidLogin_V40(Stringm_sDeviceIP,shortwPort,Stringm_sUsername,Stringm_sPassword){/*注册*///设备登录信息HCNetSDK.NET_DVR_USER_LOGIN_INFOm_strLoginInfo=newHCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备信息HCNetSDK.NET_DVR_DEVICEINFO_V40m_strDeviceInfo=newHCNetSDK.NET_DVR_DEVICEINFO_V40();m_strLoginInfo.sDeviceAddress=newbyte[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=newbyte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];System.arraycopy(m_sUsername.getBytes(),0,m_strLoginInfo.sUserName,0,m_sUsername.length());m_strLoginInfo.sPassword=newbyte[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释放publicvoidLogout(){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;}}}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-13 10:59 , Processed in 0.977151 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表