package com.ard.device.component.sdk.dh.service.impl; import com.ard.common.utils.StringUtils; import com.ard.device.camera.domain.ArdCamera; import com.ard.device.camera.domain.CameraCmd; import com.ard.device.channel.domain.ArdChannel; import com.ard.device.channel.domain.VideoCompressionCfg; import com.ard.device.component.sdk.common.GlobalVariable; import com.ard.device.component.sdk.common.osSelect; import com.ard.device.component.sdk.dh.module.ConfigModule; import com.ard.device.component.sdk.dh.module.LoginModule; import com.ard.device.component.sdk.event.LoginEvent; import com.ard.device.component.sdk.dh.lib.NetSDKLib; import com.ard.device.component.sdk.service.CameraSDK; import com.sun.jna.Native; import com.sun.jna.Pointer; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.io.*; import java.util.*; import static com.ard.device.component.sdk.dh.lib.NetSDKLib.CFG_CMD_ENCODE; import static com.ard.device.component.sdk.dh.lib.NetSDKLib.NET_DEVSTATE_ONLINE; import static com.ard.device.component.sdk.dh.lib.ToolKits.getErrorCodePrint; /** * @Description: 大华sdk策略 * @ClassName: DhSdkStrategy * @Author: 刘苏义 * @Date: 2023年11月07日10:51:09 **/ @Slf4j @Service public class DaHuaSDK implements CameraSDK { @Resource private ApplicationEventPublisher eventPublisher; public static NetSDKLib netsdk = null; public static NetSDKLib configsdk = null; // 设备断线通知回调 private static final DisConnect disConnect = new DisConnect(); // 网络连接恢复 private static final HaveReConnect haveReConnect = new HaveReConnect(); @PostConstruct public void initSdk() { try { boolean b = CreateSDKInstance(); if (!b) { log.error("Create DhSDKInstance failed"); } boolean bInit = netsdk.CLIENT_Init(disConnect, null); if (!bInit) { log.error("Initialize DhSDK failed"); } //打开日志,可选 //NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO(); //File path = new File("./sdklog/"); //if (!path.exists()) { // path.mkdir(); //} //String logPath = path.getAbsoluteFile().getParent() + "\\sdklog\\" + ToolKits.getDate() + ".log"; //setLog.nPrintStrategy = 0; //setLog.bSetFilePath = 1; //System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length); //System.out.println(logPath); //setLog.bSetPrintStrategy = 1; //bLogopen = netsdk.CLIENT_LogOpen(setLog); // if (!bLogopen) { // System.err.println("Failed to open NetSDK log"); // } // 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK内部会自动进行重连操作 // 此操作为可选操作,但建议用户进行设置 netsdk.CLIENT_SetAutoReconnect(haveReConnect, null); //设置登录超时时间和尝试次数,可选 int waitTime = 2000; //登录请求响应超时时间设置为5S int tryTimes = 1; //登录时尝试建立链接1次 netsdk.CLIENT_SetConnectTime(waitTime, tryTimes); // 设置更多网络参数,NET_PARAM的nWaitTime,nConnectTryNum成员与CLIENT_SetConnectTime // 接口设置的登录设备超时时间和尝试次数意义相同,可选 NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM(); netParam.nConnectTime = 2000; // 登录时尝试建立链接的超时时间 netParam.nGetConnInfoTime = 3000; // 设置子连接的超时时间 netParam.nGetDevInfoTime = 3000;//获取设备信息超时时间,为0默认1000ms netsdk.CLIENT_SetNetworkParam(netParam); log.info("初始化大华sdk完成"); } catch (Exception EX) { log.error("初始化大华sdk失败", EX); } } /** * 登录 */ @Override public void login(ArdCamera camera) { try { NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex(); NetSDKLib.LLong loginId = LoginModule.login(camera.getIp(), camera.getPort(), camera.getUsername(), camera.getPassword(), m_stDeviceInfo); if (loginId.longValue() <= 0) { camera.setChanNum(0); camera.setLoginId(-1L); camera.setState("0"); LoginEvent loginEvent = new LoginEvent(camera); eventPublisher.publishEvent(loginEvent); log.warn("设备[{}:{}]登录失败:{}", camera.getIp(), camera.getPort(), getErrorCodePrint()); } log.debug("设备[{}:{}]登录成功:{}", camera.getIp(), camera.getPort(), loginId); camera.setState("1"); camera.setChanNum(m_stDeviceInfo.byChanNum); camera.setStartChan(1); camera.setLoginId(loginId.longValue()); //获取最新通道 List ardChannelList = getChannels(camera); if (!ardChannelList.isEmpty()) { camera.setChannelList(ardChannelList); } LoginEvent loginEvent = new LoginEvent(camera); eventPublisher.publishEvent(loginEvent); } catch (Exception ex) { log.error("登录异常:{}", ex.getMessage()); } } /** * 注销 * 刘苏义 * 2023/10/17 8:28:13 */ @Override public void logout(ArdCamera ardCamera) { NetSDKLib.LLong loginId = new NetSDKLib.LLong(ardCamera.getLoginId()); LoginModule.logout(loginId); } /** * 是否在线检测 * 刘苏义 * 2023/10/17 8:28:13 */ @Override public boolean isOnLine(ArdCamera ardCamera) { try { NetSDKLib.LLong loginId = new NetSDKLib.LLong(ardCamera.getLoginId()); NetSDKLib.NET_WORKSTATE dh_ptz_location_info = new NetSDKLib.NET_WORKSTATE(); boolean b = ConfigModule.queryDevState(loginId, NET_DEVSTATE_ONLINE, dh_ptz_location_info); if (!b) { return false; } } catch (Exception ex) { log.error("检测在线异常:{}", ex.getMessage()); return false; } return true; } /** * 获取通道 */ @Override public List getChannels(ArdCamera camera) { if (camera.getLoginId() < 0) { return new ArrayList<>(); } NetSDKLib.LLong loginId = new NetSDKLib.LLong(camera.getLoginId()); List ardChannelList = new ArrayList<>(); for (int i = 1; i < camera.getChanNum() + 1; i++) { ArdChannel channel = new ArdChannel(); String chanName = ConfigModule.getChannelName(loginId, i).trim(); channel.setName(chanName.isEmpty() ? "通道" + i : chanName); channel.setDeviceId(camera.getId()); channel.setChanNo(i); //获取视频编码 camera.setChanNo(i); VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(camera); if (StringUtils.isNotNull(videoCompressionCfg)) { channel.setVideoCode(videoCompressionCfg.getVideoEncType()); } if (camera.getType().equals("2")) { //NVR过滤不在线的通道 if (!ConfigModule.queryCameraState(loginId, camera.getChanNum(), i)) { continue; } } ardChannelList.add(channel); } return ardChannelList; } @Override public VideoCompressionCfg getVideoCompressionCfg(ArdCamera ardCamera) { NetSDKLib.LLong loginId = new NetSDKLib.LLong(ardCamera.getLoginId()); NetSDKLib.CFG_ENCODE_INFO cfg_encode_info = new NetSDKLib.CFG_ENCODE_INFO(); cfg_encode_info.nChannelID = ardCamera.getChanNo() - 1; boolean b = ConfigModule.GetNewDevConfig(loginId, ardCamera.getChanNo() - 1, CFG_CMD_ENCODE, cfg_encode_info); if (b) { int emCompression = cfg_encode_info.stuMainStream[0].stuVideoFormat.emCompression;//视频格式 String videoEncType; switch (emCompression) { case 0: videoEncType = "MPEG4"; break; case 1: videoEncType = "MS-MPEG4"; break; case 2: videoEncType = "MPEG2"; break; case 3: videoEncType = "MPEG1"; break; case 4: videoEncType = "H.263"; break; case 5: videoEncType = "MJPG"; break; case 6: videoEncType = "FCC-MPEG4"; break; case 7: videoEncType = "标准h264"; break; case 8: videoEncType = "标准h265"; break; case 9: videoEncType = "SVAC"; break; default: videoEncType = "未知"; break; } int nBitRate = cfg_encode_info.stuMainStream[0].stuVideoFormat.nBitRate; int nWidth = cfg_encode_info.stuMainStream[0].stuVideoFormat.nWidth; int nHeight = cfg_encode_info.stuMainStream[0].stuVideoFormat.nHeight; String resolution = nWidth + "*" + nHeight; float nFrameRate = cfg_encode_info.stuMainStream[0].stuVideoFormat.nFrameRate; VideoCompressionCfg videoCompressionCfg = new VideoCompressionCfg(); videoCompressionCfg.setVideoBitrate(String.valueOf(nBitRate));//比特率 videoCompressionCfg.setResolution(resolution);//分辨率 videoCompressionCfg.setVideoEncType(videoEncType);//编码 videoCompressionCfg.setFrameRate(String.valueOf(nFrameRate));//帧率 return videoCompressionCfg; } else { log.error("获取码流压缩参数失败:{}", getErrorCodePrint()); return null; } } /** * 动态库加载 - 大华 NetSDK + ConfigSDK */ private static boolean CreateSDKInstance() { if (netsdk == null || configsdk == null) { synchronized (NetSDKLib.class) { String netSdkDllPath = ""; String configSdkDllPath = ""; try { String userDir = System.getProperty("user.dir"); if (osSelect.isWindows()) { netSdkDllPath = userDir + "\\lib\\dh\\win64\\dhnetsdk.dll"; configSdkDllPath = userDir + "\\lib\\dh\\win64\\dhconfigsdk.dll"; } else if (osSelect.isLinux()) { if (osSelect.isArm64()) { netSdkDllPath = userDir + "/lib/dh/linux-arm64/libdhnetsdk.so"; configSdkDllPath = userDir + "/lib/dh/linux-arm64/libdhconfigsdk.so"; } else if (osSelect.isX64()) { netSdkDllPath = userDir + "/lib/dh/linux64/libdhnetsdk.so"; configSdkDllPath = userDir + "/lib/dh/linux64/libdhconfigsdk.so"; } else { throw new UnsupportedOperationException("❌ 不支持的 Linux 架构: " + osSelect.getArch()); } } else { throw new UnsupportedOperationException("❌ 不支持的操作系统: " + osSelect.getOsName()); } // 检查文件是否存在 if (!new File(netSdkDllPath).exists()) { System.err.println("❌ dhnetsdk 库文件不存在: " + netSdkDllPath); return false; } if (!new File(configSdkDllPath).exists()) { System.err.println("❌ dhconfigsdk 库文件不存在: " + configSdkDllPath); return false; } System.out.println("⏳ 正在加载 NetSDK: " + netSdkDllPath); netsdk = Native.loadLibrary(netSdkDllPath, NetSDKLib.class); System.out.println("⏳ 正在加载 ConfigSDK: " + configSdkDllPath); configsdk = Native.loadLibrary(configSdkDllPath, NetSDKLib.class); System.out.println("✅ SDK 加载完成"); } catch (Exception ex) { System.err.println("❌ SDK 加载失败: \n异常: " + ex.getMessage()); ex.printStackTrace(); return false; } } } return true; } // 设备断线回调: 当设备出现断线时,SDK会调用该函数 private static class DisConnect implements NetSDKLib.fDisConnect { @Override public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) { log.warn("Device[" + pchDVRIP + ":" + nDVRPort + "] DisConnect!"); } } // 网络连接恢复回调:设备重连成功回调,当已断线的设备重连成功时,SDK会调用该函数 private static class HaveReConnect implements NetSDKLib.fHaveReConnect { @Override public void invoke(NetSDKLib.LLong lLoginID, String pchDVRIP, int nDVRPort, Pointer dwUser) { log.warn("ReConnect Device[" + pchDVRIP + ":" + nDVRPort + "]"); } } }