海康解码器对接总结(java 版)
本文只是对接海康解码器的动态解码功能,即配置解码器大屏上指定的某个窗口去解某一路IP视频源。
1. 首先,定义所需的结构体与接口。海康SDK中包含的结构体与接口非常之多,在官方的例子中,实现了大部分的结构体与接口,导致一个类的代码行数高达4000多行。但是我们不必每一个都实现,我们只需要实现需要用到的结构体与接口即可,如下:
package darwin.soc; import com.sun.jna.*; import com.sun.jna.ptr.IntByReference; import java.util.Arrays; import java.util.List; //SDK接口说明,HCNetSDK.dll public interface HCNetSDK extends Library { HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("hcnetsdk", HCNetSDK.class); /*** 宏定义***/ public static final int SERIALNO_LEN = 48; public static final int MAX_DOMAIN_NAME = 64; public static final int STREAM_ID_LEN = 32; public static final int NAME_LEN = 32; public static final int PASSWD_LEN = 16; public static final int URL_LEN = 240; /*** 结构体定义***/ public static class NET_DVR_DEVICEINFO_V30 extends Structure{ public byte[] sSerialNumber = new byte[SERIALNO_LEN]; public byte byAlarmInPortNum; public byte byAlarmOutPortNum; public byte byDiskNum; public byte byDVRType; public byte byChanNum; public byte byStartChan; public byte byAudioChanNum; public byte byIPChanNum; public byte[] byRes1 = new byte[24]; @Override protected List<String> getFieldOrder() { return Arrays.asList("sSerialNumber","byAlarmInPortNum","byAlarmOutPortNum", "byDiskNum","byDVRType","byChanNum","byStartChan","byAudioChanNum", "byIPChanNum","byRes1"); } } public static class NET_DVR_PU_STREAM_CFG_V41 extends Structure{ public int dwSize; public byte byStreamMode; public byte[] byRes1 = new byte[3]; public NET_DVR_DEC_STREAM_MODE uDecStreamMode; public byte[] byRes2 = new byte[64]; @Override protected List<String> getFieldOrder() { return Arrays.asList("dwSize","byStreamMode","byRes1","uDecStreamMode","byRes2"); } } public static class NET_DVR_DEC_STREAM_MODE extends Union{ public NET_DVR_DEC_STREAM_DEV_EX struDecStreamDev; public NET_DVR_PU_STREAM_URL struUrlInfo; public NET_DVR_DEC_DDNS_DEV struDdnsDecInfo; public byte[] byRes = new byte[300]; @Override protected List<String> getFieldOrder() { return Arrays.asList("struDecStreamDev","struUrlInfo","struDdnsDecInfo","byRes"); } } public static class NET_DVR_DEC_STREAM_DEV_EX extends Structure{ public NET_DVR_STREAM_MEDIA_SERVER struStreamMediaSvrCfg; public NET_DVR_DEV_CHAN_INFO_EX struDevChanInfo; @Override protected List<String> getFieldOrder() { return Arrays.asList("struStreamMediaSvrCfg","struDevChanInfo"); } } public static class NET_DVR_PU_STREAM_URL extends Structure{ public byte byEnable; public byte[] strURL = new byte[URL_LEN]; public byte byTransPortocol; public short wIPID; public byte byChannel; public byte[] byRes = new byte[7]; @Override protected List<String> getFieldOrder() { return Arrays.asList("byEnable","strURL","byTransPortocol","wIPID","byChannel","byRes"); } } public static class NET_DVR_DEC_DDNS_DEV extends Structure{ public NET_DVR_DEV_DDNS_INFO struDdnsInfo; public NET_DVR_STREAM_MEDIA_SERVER struMediaServer; @Override protected List<String> getFieldOrder() { return Arrays.asList("struDdnsInfo","struMediaServer"); } } public static class NET_DVR_STREAM_MEDIA_SERVER extends Structure{ public byte byValid; public byte[] byRes1 = new byte[3]; public byte[] byAddress = new byte[MAX_DOMAIN_NAME]; public short wDevPort; public byte byTransmitType; public byte[] byRes2 = new byte[5]; @Override protected List<String> getFieldOrder() { return Arrays.asList("byValid","byRes1","byAddress","wDevPort","byTransmitType","byRes2"); } } public static class NET_DVR_DEV_CHAN_INFO_EX extends Structure{ public byte byChanType; public byte[] byStreamId = new byte[STREAM_ID_LEN]; public byte[] byRes1 = new byte[3]; public int dwChannel; public byte[] byRes2 = new byte[24]; public byte[] byAddress = new byte[MAX_DOMAIN_NAME]; public short wDVRPort; public byte byChannel; public byte byTransProtocol; public byte byTransMode; public byte byFactoryType; public byte byDeviceType; public byte byDispChan; public byte bySubDispChan; public byte byResolution; public byte[] byRes = new byte[2]; public byte[] sUserName = new byte[NAME_LEN]; public byte[] sPassword = new byte[PASSWD_LEN]; @Override protected List<String> getFieldOrder() { return Arrays.asList("byChanType","byStreamId","byRes1","dwChannel","byRes2","byAddress","wDVRPort", "byChannel","byTransProtocol","byTransMode","byFactoryType","byDeviceType","byDispChan", "bySubDispChan","byResolution","byRes","sUserName","sPassword"); } } public static class NET_DVR_DEV_DDNS_INFO extends Structure{ public byte[] byDevAddress = new byte[MAX_DOMAIN_NAME]; public byte byTransProtocol; public byte byTransMode; public byte byDdnsType; public byte byRes1; public byte[] byDdnsAddress = new byte[MAX_DOMAIN_NAME]; public short wDdnsPort; public byte byChanType; public byte byFactoryType; public short dwChannel; public byte[] byStreamId = new byte[STREAM_ID_LEN]; public byte[] sUserName = new byte[NAME_LEN]; public byte[] sPassword = new byte[PASSWD_LEN]; public short wDevPort; public byte[] byRes2 = new byte[2]; @Override protected List<String> getFieldOrder() { return Arrays.asList("byDevAddress","byTransProtocol","byTransMode","byDdnsType","byRes1","byDdnsAddress", "wDdnsPort","byChanType","byFactoryType","dwChannel","byStreamId","sUserName","sPassword", "wDevPort","byRes2"); } } /*** 接口定义 ***/ /** * 登录设备 * */ NativeLong NET_DVR_Login_V30(String sDVRIP, short wDVRPort, String sUserName, String sPassword, NET_DVR_DEVICEINFO_V30 lpDeviceInfo); /** * 退出登录 * */ boolean NET_DVR_Logout(NativeLong lUserID); /** * 初始化SDK * */ boolean NET_DVR_Init(); /** * 清理资源 * */ boolean NET_DVR_Cleanup(); /** * 获取错误码 * */ int NET_DVR_GetLastError(); /** * 重启设备 * */ boolean NET_DVR_RebootDVR(NativeLong lUserID); /** * 动态解码 * */ boolean NET_DVR_MatrixStartDynamic_V41(NativeLong lUserID,int dwDecChanNum,Pointer lpDynamicInfo); /** * 获取窗口解码开关 * */ boolean NET_DVR_MatrixGetDecChanEnable(NativeLong lUserID, int dwDecChanNum, IntByReference lpdwEnable); }
2.开始编写入口类,调用NET_DVR_MatrixStartDynamic_V41方法,如下:
package darwin.soc; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; /** * @copyright: Copyright (c) 2015-2025 * @company: * @author: hjw * @date: 2018-02-02 17:21 * @description: 加载海康SDK的工具类 */ public class LoadSDKUtil { private static HCNetSDK hcNetSDK = HCNetSDK.INSTANCE; //SDK初始化成功的标志 private static boolean initSDKSuccess = false; static { initSDKSuccess = hcNetSDK.NET_DVR_Init(); } /** * @Author: hjw * @Date: 2018-02-05 14:16 * @Param: * @Return: * @Description: 登录设备 */ public static long loginDevice(String ip,int port,String userName,String password){ if(!initSDKSuccess){ return -1; } HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceinfo_v30 = new HCNetSDK.NET_DVR_DEVICEINFO_V30(); NativeLong lUserID = hcNetSDK.NET_DVR_Login_V30(ip,(short) port,userName,password,deviceinfo_v30); return lUserID.longValue(); } //拿到信号源信息结构体的指针 public static Pointer getIPCInfoPointer(String ipAddress,short port,String userName,String passWord){ //动态解码 HCNetSDK.NET_DVR_PU_STREAM_CFG_V41 m_struDynamicInfo = new HCNetSDK.NET_DVR_PU_STREAM_CFG_V41(); m_struDynamicInfo.dwSize = m_struDynamicInfo.size(); //直接从设备取流 m_struDynamicInfo.byStreamMode=(byte)1; //联合体需要设置Type m_struDynamicInfo.uDecStreamMode.setType(HCNetSDK.NET_DVR_DEC_STREAM_DEV_EX.class); m_struDynamicInfo.uDecStreamMode.read(); //前端设 备 IP 地址 for(int i = 0; i < HCNetSDK.MAX_DOMAIN_NAME; i++){ m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.byAddress[i] = 0; } for(int i = 0; i < ipAddress.length(); i++){ m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.byAddress[i] = ipAddress.getBytes()[i]; } //前端设备服务端口 m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.wDVRPort = port; //前端设备登 录用户名 for(int i = 0; i < HCNetSDK.NAME_LEN; i++){ m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.sUserName[i] = 0; } for(int i = 0; i < userName.length(); i++){ m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.sUserName[i] = userName.getBytes()[i]; } //前端设备登 录密码 for(int i = 0; i < HCNetSDK.PASSWD_LEN; i++){ m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.sPassword[i] = 0; } for(int i = 0; i < passWord.length(); i++){ m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.sPassword[i] = passWord.getBytes()[i]; } //取通道 1 的码流 m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.byChannel =(byte) 1; //厂家私有协议 m_struDynamicInfo.uDecStreamMode.struDecStreamDev.struDevChanInfo.byFactoryType =(byte) 0; m_struDynamicInfo.write(); Pointer pointer = m_struDynamicInfo.getPointer(); return pointer; } //动态解码 public static void startDynamicDecode(NativeLong lUserID,int winNo,String ipAddress,short port,String userName, String passWord){ //视频源信息结构体指针 Pointer pointer = getIPCInfoPointer(ipAddress,port,userName,passWord); //窗口号,代表第一组大屏第winNo个窗口 int dwChannelEx = 1<<24|1<<16|winNo; boolean flag = hcNetSDK.NET_DVR_MatrixStartDynamic_V41(lUserID,dwChannelEx, pointer); if(!flag){ int errorCode = hcNetSDK.NET_DVR_GetLastError(); System.out.println("动态解码出错:" + errorCode); } } public static void main(String[] args) throws InterruptedException { //登录解码器(解码器的用户名与密码) long userId = loginDevice("192.168.10.77",8000,"admin","hu328704547"); System.out.println("userId:" + userId); NativeLong lUserID = new NativeLong(userId); if(userId == -1){ int errorCode = hcNetSDK.NET_DVR_GetLastError(); System.out.println("Error Code:" + errorCode); } //解码 try { String ip = "192.168.10.66"; short port = 8000; String userName = "admin"; String passWord = "1qaz2wsx"; /* String ip = "192.168.10.65"; short port = 8000; String userName = "admin"; String passWord = "admin1234567890";*/ startDynamicDecode(lUserID,1,ip,port,userName,passWord); }catch (Exception e){ e.printStackTrace(); } hcNetSDK.NET_DVR_Logout(lUserID); hcNetSDK.NET_DVR_Cleanup(); System.out.println("ended!!!"); } }
需要注意的是:
1.NET_DVR_PU_STREAM_CFG_V41结构体中的uDecStreamMode属性不是一个结构体,而是一个Union,所以uDecStreamMode属性具体使用其下面哪一个属性,需要使用m_struDynamicInfo.uDecStreamMode.setType(HCNetSDK.NET_DVR_DEC_STREAM_DEV_EX.class); 来进行指定。
2.当结构体中的某个属性是字节数组时,不应该使用getBytes()方法直接赋值(因为这样会导致字节数组的长度发生变化),而是如我上面的代码片段那样循环来为字节数组中的每一个字节赋值。