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<ArdChannel> 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<ArdChannel> getChannels(ArdCamera camera) {
|
if (camera.getLoginId() < 0) {
|
return new ArrayList<>();
|
}
|
NetSDKLib.LLong loginId = new NetSDKLib.LLong(camera.getLoginId());
|
List<ArdChannel> 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 + "]");
|
}
|
}
|
}
|