package com.ard.work.sdk.dh.service;
|
|
import com.ard.common.core.constant.CacheConstants;
|
import com.ard.common.core.domain.R;
|
import com.ard.common.core.exception.ServiceException;
|
import com.ard.common.core.exception.device.CameraSDKException;
|
import com.ard.common.core.utils.StringUtils;
|
import com.ard.common.core.utils.file.FileUtils;
|
import com.ard.common.core.utils.file.MimeTypeUtils;
|
import com.ard.common.redis.service.RedisService;
|
import com.ard.system.api.RemoteFileService;
|
import com.ard.system.api.domain.SysFile;
|
import com.ard.work.api.domian.*;
|
import com.ard.work.device.camera.domain.PtzParamDTO;
|
import com.ard.work.device.terrain.domain.ArdTerrainMark;
|
import com.ard.work.device.terrain.service.IArdTerrainMarkService;
|
import com.ard.work.event.LoginEvent;
|
import com.ard.work.sdk.common.GlobalVariable;
|
import com.ard.work.sdk.common.osSelect;
|
import com.ard.work.sdk.dh.lib.NetSDKLib;
|
import com.ard.work.sdk.dh.lib.enumeration.EM_NEW_CONFIG;
|
import com.ard.work.sdk.dh.lib.enumeration.NET_EM_CFG_OPERATE_TYPE;
|
import com.ard.work.sdk.dh.lib.structure.CFG_INFRARED_INFO;
|
import com.ard.work.sdk.dh.lib.structure.CFG_VIDEO_IN_FOCUS;
|
import com.ard.work.sdk.dh.lib.structure.DH_OUT_PTZ_VIEW_RANGE_STATUS;
|
import com.ard.work.sdk.dh.lib.structure.NET_VIDEOIN_DEFOG_INFO;
|
import com.ard.work.sdk.dh.module.*;
|
import com.ard.work.sdk.model.DeviceCFG;
|
import com.ard.work.sdk.model.PtzScope;
|
import com.ard.work.sdk.service.CameraSDK;
|
import com.ard.work.utils.FFmpegUtils;
|
import com.ard.work.utils.file.MultipartFileUtil;
|
import com.ard.work.utils.gis.GisUtil;
|
import com.sun.jna.Native;
|
import com.sun.jna.Pointer;
|
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.StopWatch;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.io.ByteArrayInputStream;
|
import java.io.File;
|
import java.io.InputStream;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.OptionalDouble;
|
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.ExecutionException;
|
import java.util.stream.Collectors;
|
|
import static com.ard.work.sdk.dh.lib.NetSDKLib.*;
|
import static com.ard.work.sdk.dh.lib.NetSDKLib.NET_PTZ_ControlType.NET_PTZ_POINT_MOVE_CONTROL;
|
import static com.ard.work.sdk.dh.lib.NetSDKLib.NET_PTZ_ControlType.NET_PTZ_POINT_SET_CONTROL;
|
import static com.ard.work.sdk.dh.lib.ToolKits.getErrorCodePrint;
|
|
/**
|
* @Description: 大华sdk策略
|
* @ClassName: DhSdkStrategy
|
* @Author: 刘苏义
|
* @date: 2023年11月07日10:51:09
|
**/
|
@Service("dahuaSDK")
|
@Slf4j
|
public class DaHuaSDK implements CameraSDK {
|
@Resource
|
private ApplicationEventPublisher eventPublisher;
|
|
@Resource
|
private RedisService redisService;
|
|
@Resource
|
private RemoteFileService remoteFileService;
|
|
@Resource
|
private IArdTerrainMarkService ardTerrainMarkService;
|
|
@Value("${sdk.tempDir}")
|
private String tempDir;
|
|
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);
|
}
|
}
|
|
/**
|
* 登录
|
* 刘苏义
|
* 2023/10/17 8:28:13Wde2486Eds
|
*/
|
@Override
|
public void login(ArdCamera camera) {
|
String ip = camera.getIp();
|
Integer port = camera.getPort();
|
String username = camera.getUsername();
|
String password = camera.getPassword();
|
|
NetSDKLib.NET_DEVICEINFO_Ex deviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
|
NetSDKLib.LLong loginId = LoginModule.login(ip, port, username, password, deviceInfo);
|
if (loginId.longValue() <= 0) {
|
// 登录失败处理
|
handleLoginFail(camera, loginId);
|
} else {
|
// 登录成功处理
|
handleLoginSuccess(camera, loginId, deviceInfo);
|
}
|
// 发布登录事件
|
eventPublisher.publishEvent(new LoginEvent(camera));
|
}
|
|
/**
|
* 登录失败处理
|
*/
|
private void handleLoginFail(ArdCamera camera, NetSDKLib.LLong loginId) {
|
camera.setChanNum(0);
|
camera.setLoginId(loginId.longValue());
|
camera.setState("0");
|
camera.setChannelList(null);
|
log.warn("设备[{}:{}]登录失败,{}", camera.getIp(), camera.getPort(), getErrorCodePrint());
|
}
|
|
/**
|
* 登录成功处理
|
*/
|
private void handleLoginSuccess(ArdCamera camera, NetSDKLib.LLong loginId, NetSDKLib.NET_DEVICEINFO_Ex deviceInfo) {
|
camera.setState("1");
|
camera.setChanNum(deviceInfo.byChanNum);
|
camera.setStartChan(1);
|
camera.setLoginId(loginId.longValue());
|
//获取通道并设置
|
List<ArdChannel> channels = getChannels(camera);
|
if (!channels.isEmpty()) {
|
camera.setChannelList(channels);
|
}
|
log.info("设备登录成功 [{}:{}]", camera.getIp(), camera.getPort());
|
}
|
|
//获取通道
|
@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);
|
//获取视频编码
|
CameraCmd cameraCmd = new CameraCmd(camera.getId(), channel.getChanNo());
|
cameraCmd.setLoginId(camera.getLoginId());
|
VideoCompressionCfg videoCompressionCfg = getVideoCompressionCfg(cameraCmd);
|
if (StringUtils.isNotNull(videoCompressionCfg)) {
|
channel.setVideoCode(videoCompressionCfg.getVideoEncType());
|
}
|
if ("2".equals(camera.getType())) {
|
//NVR过滤不在线的通道
|
if (!ConfigModule.queryCameraState(loginId, camera.getChanNum(), i)) {
|
continue;
|
}
|
}
|
|
String rtspUrl = String.format("rtsp://%s:%s@%s:%d/cam/realmonitor?channel=%d&subtype=0",
|
camera.getUsername(),
|
camera.getPassword(),
|
camera.getIp(),
|
camera.getRtspPort(),
|
channel.getChanNo()
|
);
|
channel.setLiveAddress(rtspUrl);
|
ardChannelList.add(channel);
|
}
|
return ardChannelList;
|
}
|
|
/**
|
* 注销
|
* 刘苏义
|
* 2023/10/17 8:28:13
|
*/
|
@Override
|
public void logout(String CameraId) {
|
Long loginId = redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, CameraId);
|
if (loginId == null) {
|
return;
|
}
|
LoginModule.logout(new NetSDKLib.LLong(loginId));
|
}
|
|
|
/**
|
* 是否在线检测
|
* 刘苏义
|
* 2023/10/17 8:28:13
|
*/
|
@Override
|
public Boolean isOnLine(CameraCmd cmd) {
|
try {
|
LLong loginId = new LLong(cmd.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 Boolean pTZControl(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
boolean enable = cmd.isEnable();
|
int chanNo = cmd.getChanNo(); // 通道号
|
int speed = cmd.getSpeed(); // 速度
|
int code = cmd.getCode(); // 指令
|
NetSDKLib.LLong loginId = new NetSDKLib.LLong(cmd.getLoginId());
|
|
int dwStop = enable ? 0 : 1; // 0 开启,1 关闭
|
int dwPTZCommand;
|
|
// 1️⃣ 根据 code 映射控制类型
|
switch (code) {
|
case 1:
|
dwPTZCommand = NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_LEFTTOP;
|
break;
|
case 2:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_UP_CONTROL;
|
break;
|
case 3:
|
dwPTZCommand = NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_RIGHTTOP;
|
break;
|
case 4:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_LEFT_CONTROL;
|
break;
|
case 5:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_POINT_LOOP_CONTROL;
|
break;
|
case 6:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_RIGHT_CONTROL;
|
break;
|
case 7:
|
dwPTZCommand = NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_LEFTDOWN;
|
break;
|
case 8:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_DOWN_CONTROL;
|
break;
|
case 9:
|
dwPTZCommand = NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_RIGHTDOWN;
|
break;
|
case 10:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_ZOOM_ADD_CONTROL;
|
break;
|
case 11:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_ZOOM_DEC_CONTROL;
|
break;
|
case 12:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_FOCUS_ADD_CONTROL;
|
break;
|
case 13:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_FOCUS_DEC_CONTROL;
|
break;
|
case 14:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_APERTURE_ADD_CONTROL;
|
break;
|
case 15:
|
dwPTZCommand = NetSDKLib.NET_PTZ_ControlType.NET_PTZ_APERTURE_DEC_CONTROL;
|
break;
|
case 16:
|
dwPTZCommand = NetSDKLib.CtrlType.CTRLTYPE_CTRL_RAINBRUSH_MOVEONCE;
|
break;
|
default:
|
log.warn("【{}】PTZ控制指令不支持:{}", cameraId, code);
|
return false;
|
}
|
|
// 2️⃣ 调用 SDK 控制
|
boolean success;
|
if (code == 16) {
|
// 雨刷控制单独处理
|
success = PtzControlModule.controlRainBrushControl(loginId, chanNo - 1, dwPTZCommand);
|
} else {
|
success = PtzControlModule.ptzControl(loginId, chanNo - 1, dwPTZCommand, speed, speed, 0, dwStop);
|
}
|
|
// 3️⃣ 判断控制结果
|
if (!success) {
|
log.warn("【{}】PTZ控制失败,错误码:{}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
return true; // 成功
|
}
|
|
|
/**
|
* 获取PTZ值
|
* 刘苏义
|
* 2023/10/17 8:28:13
|
*/
|
@Override
|
public PtzDto getPtz(CameraCmd cmd) {
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
NetSDKLib.NET_PTZ_LOCATION_INFO dh_ptz_location_info = new NetSDKLib.NET_PTZ_LOCATION_INFO();
|
boolean b = ConfigModule.queryDevState(loginId, NET_DEVSTATE_PTZ_LOCATION, dh_ptz_location_info);
|
if (!b) {
|
String errorMsg = String.format("相机【%s】获取ptz失败,%s", cmd.getCameraId(), getErrorCodePrint());
|
log.warn(errorMsg);
|
return null;
|
}
|
// Pan (-1800~1800 → -180~180)
|
float P = dh_ptz_location_info.nPTZPan / 10f;
|
// Tilt (-1800~1800 → -180~180 → 0~360)
|
float tilt = dh_ptz_location_info.nPTZTilt / -10f; // 如果设备方向要反
|
float T = Math.round(((tilt + 360f) % 360f) * 100f) / 100f;
|
// Zoom (放大100倍 → 真实值)
|
float Z = dh_ptz_location_info.nZoomValue / 100f;
|
return new PtzDto(P, T, Z);
|
}
|
|
@Override
|
public PtzDto getPtzHigh(CameraCmd cmd) {
|
return getPtz(cmd);
|
}
|
|
@Override
|
public PtzScope getPtzScope(CameraCmd cmd) {
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
NET_PRODUCTION_DEFNITION productionDefinition = new NET_PRODUCTION_DEFNITION();
|
boolean b = ConfigModule.QueryProductionDefinition(loginId, productionDefinition);
|
if (!b) {
|
String errorMsg = String.format("获取ptz范围失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return null;
|
}
|
PtzScope ptzScope = new PtzScope();
|
ptzScope.setPMax((float) productionDefinition.nPtzHorizontalAngleMax);
|
ptzScope.setPMin((float) productionDefinition.nPtzHorizontalAngleMin);
|
ptzScope.setTMax((float) productionDefinition.nPtzVerticalAngleMax);
|
ptzScope.setTMin((float) productionDefinition.nPtzVerticalAngleMin);
|
return ptzScope;
|
}
|
|
|
@Override
|
public Boolean setPtzHigh(CameraCmd cmd) {
|
return setPtz(cmd);
|
}
|
|
@Override
|
public Boolean setPtzHighT(CameraCmd cmd) {
|
return setPtz(cmd);
|
}
|
|
//设置ptz信息
|
@Override
|
public Boolean setPtz(CameraCmd cmd) {
|
Integer chanNo = cmd.getChanNo();
|
PtzDto ptzDto = cmd.getPtzDto();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
try {
|
int p = Math.round(ptzDto.getP() * 10);
|
int t = Math.round(ptzDto.getT() * 10);
|
t = (t > 900) ? (3600 - t) : (-t);
|
float scale = ptzDto.getZ();
|
int z = convertZoomTo128Range(scale);
|
boolean bool = netsdk.CLIENT_DHPTZControlEx2(loginId, chanNo - 1,
|
NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_EXACTGOTO, p, t, z, 0, null);
|
if (!bool) {
|
String errorMsg = String.format("设置ptz失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
} catch (Exception ex) {
|
log.error("设置ptz异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
|
/**
|
* 将float类型的实际倍率(0~86)转换为0~128区间的整数数值
|
*
|
* @param actualZoom 实际倍率值(float类型,0~86)
|
* @return 0~128区间的整数结果
|
*/
|
public static int convertZoomTo128Range(float actualZoom) {
|
// 边界值校验(避免超出范围)
|
if (actualZoom < 0.0f) {
|
return 0;
|
}
|
if (actualZoom > 86.0f) {
|
return 128;
|
}
|
// 核心转换计算,四舍五入取整
|
double result = (double) actualZoom / 86.0 * 128.0;
|
return (int) Math.round(result);
|
}
|
|
//设置零方位角
|
@Override
|
public Boolean setZeroPtz(CameraCmd cmd) {
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
boolean bool = netsdk.CLIENT_DHPTZControlEx(loginId, chanNo - 1,
|
NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_RESETZERO, 0, 0, 0, 0);
|
if (!bool) {
|
String errorMsg = String.format("设置零方位角失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
}
|
|
|
//截图 存服务器
|
@Override
|
public String picCutCate(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String bucketName = cmd.getBucketName() == null ? "pic" : cmd.getBucketName();
|
String type = cmd.getType() == null ? "other" : cmd.getType();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
String url = "";
|
|
String objectName = cameraId + "_" + chanNo + ".jpeg";
|
CompletableFuture<String> urlFuture = new CompletableFuture<>();
|
fCaptureReceiveCB m_CaptureReceiveCB = new fCaptureReceiveCB(bucketName, objectName, type, urlFuture);
|
CapturePictureModule.setSnapRevCallBack(m_CaptureReceiveCB);
|
boolean success = CapturePictureModule.remoteCapturePicture(loginId, chanNo - 1);
|
if (!success) {
|
String errorMsg = String.format("截图失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return url;
|
}
|
// 阻塞等待,直到异步任务完成
|
try {
|
return urlFuture.get();
|
} catch (InterruptedException | ExecutionException e) {
|
log.error(e.getMessage());
|
}
|
return url;
|
}
|
|
|
//截图 存服务器
|
@Override
|
public String picCutCate(CameraCmd cmd, String bucketName, String objectName) {
|
Integer chanNo = cmd.getChanNo();
|
String type = cmd.getType() == null ? "other" : cmd.getType();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
String url = "";
|
CompletableFuture<String> urlFuture = new CompletableFuture<>();
|
fCaptureReceiveCB m_CaptureReceiveCB = new fCaptureReceiveCB(bucketName, objectName, type, urlFuture);
|
CapturePictureModule.setSnapRevCallBack(m_CaptureReceiveCB);
|
boolean success = CapturePictureModule.remoteCapturePicture(loginId, chanNo - 1);
|
if (!success) {
|
String errorMsg = String.format("截图失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return url;
|
}
|
// 阻塞等待,直到异步任务完成
|
try {
|
return urlFuture.get();
|
} catch (InterruptedException | ExecutionException e) {
|
log.error(e.getMessage());
|
}
|
return url;
|
}
|
|
@Override
|
public byte[] localCapture(CameraCmd cmd) {
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
CompletableFuture<byte[]> imageFuture = new CompletableFuture<>();
|
fCaptureReceiveBytes m_CaptureReceiveCB = new fCaptureReceiveBytes(imageFuture);
|
CapturePictureModule.setSnapRevCallBack(m_CaptureReceiveCB);
|
boolean success = CapturePictureModule.remoteCapturePicture(loginId, chanNo - 1);
|
if (!success) {
|
log.warn("截图失败,{}", getErrorCodePrint());
|
return null;
|
}
|
// 阻塞等待,直到异步任务完成
|
try {
|
return imageFuture.get();
|
} catch (InterruptedException | ExecutionException e) {
|
throw new RuntimeException(e);
|
}
|
}
|
|
@Override
|
public Double correctPitch(CameraCmd cmd) {
|
double pitchAngle = 0.0;
|
try {
|
Point targetPosition = cmd.getTargetPosition();
|
ArdTerrainMark ardTerrainMark = new ArdTerrainMark();
|
ardTerrainMark.setCameraId(cmd.getCameraId());
|
List<ArdTerrainMark> ardTerrainMarks = ardTerrainMarkService.selectArdTerrainMarkList(ardTerrainMark);
|
//过滤500米范围内的集合
|
ardTerrainMarks = ardTerrainMarks.stream().filter(n -> GisUtil.getDistance(targetPosition,
|
new Point(n.getLongitude(), n.getLatitude(), n.getAltitude())) <= 500).collect(Collectors.toList());
|
//计算T平均值
|
OptionalDouble averageT = ardTerrainMarks.stream().mapToDouble(ArdTerrainMark::getT).average();
|
// 输出
|
if (averageT.isPresent()) {
|
pitchAngle = averageT.getAsDouble();
|
log.debug("修正俯仰值:{}", pitchAngle);
|
}
|
} catch (Exception ex) {
|
log.error("修正俯仰值异常:{}", ex.getMessage());
|
|
}
|
return pitchAngle;
|
}
|
|
//短时录像
|
@Override
|
public String record(CameraCmd cmd) {
|
try {
|
String url = "";
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
|
// 本地临时录像地址
|
String tempPicPath = tempDir + "/record/" + name + ".mp4";
|
String path = FileUtils.createFile(tempPicPath);
|
|
boolean enable = cmd.isEnable();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
|
if (enable) {
|
LLong lRealHandle;
|
if (!GlobalVariable.previewMap.containsKey(name)) {
|
lRealHandle = RealPlayModule.startRealPlay(loginId, chanNo - 1, path);
|
if (lRealHandle.longValue() <= 0) {
|
String errorMsg = String.format("取流失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return "";
|
}
|
log.debug("取流成功");
|
GlobalVariable.previewMap.put(name, lRealHandle);
|
}
|
log.debug("录像开始");
|
} else {
|
if (GlobalVariable.previewMap.containsKey(name)) {
|
LLong lRealHandle = (LLong) (GlobalVariable.previewMap.get(name));
|
RealPlayModule.stopRealPlay(lRealHandle);
|
GlobalVariable.previewMap.remove(name);
|
}
|
log.debug("录像停止");
|
}
|
return url;
|
} catch (Exception ex) {
|
log.error("录像异常:{}", ex.getMessage());
|
return "";
|
}
|
}
|
|
@Override
|
public Boolean recordStart(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
// 本地临时录像地址
|
String tempPicPath = tempDir + "/record/" + name + ".mp4";
|
String path = FileUtils.createFile(tempPicPath);
|
|
LLong lRealHandle;
|
if (!GlobalVariable.previewMap.containsKey(name)) {
|
lRealHandle = RealPlayModule.startRealPlay(loginId, chanNo - 1, path);
|
if (lRealHandle.longValue() <= 0) {
|
String errorMsg = String.format("取流失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return false;
|
}
|
log.debug("取流成功");
|
GlobalVariable.previewMap.put(name, lRealHandle);
|
}
|
log.debug("录像开始");
|
return true;
|
} catch (Exception ex) {
|
log.error("开始录像异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
|
@Override
|
public String recordStop(CameraCmd cmd) {
|
String url = "";
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
String name = cameraId + "_" + chanNo;
|
|
// 本地临时录像地址
|
String tempPicPath = tempDir + "/record/" + name + ".mp4";
|
String path = FileUtils.createFile(tempPicPath);
|
|
LLong lRealHandle;
|
if (GlobalVariable.previewMap.containsKey(name)) {
|
lRealHandle = (LLong) (GlobalVariable.previewMap.get(name));
|
RealPlayModule.stopRealPlay(lRealHandle);
|
GlobalVariable.previewMap.remove(name);
|
log.debug("停止当前录像");
|
}
|
//ffmpeg转码
|
StopWatch watchStop = new StopWatch();
|
watchStop.start();
|
String newpath = FileUtils.createFile(tempDir + "/record/" + name + "_c.mp4");
|
FFmpegUtils.transcodeToMP4(path, newpath);
|
watchStop.stop();
|
log.warn("转码耗时:{}", watchStop.getTotalTimeMillis());
|
//存入minio
|
String bucketName = "record";
|
String objectName = cameraId + "_" + chanNo;
|
String type = cmd.getType();
|
MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(newpath, objectName,
|
MimeTypeUtils.VIDEO_MP4);
|
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
|
if (sysFileR.getCode() == R.SUCCESS) {
|
url = sysFileR.getData().getUrl();
|
log.info("上传文件成功:{}", url);
|
}
|
} catch (Exception ex) {
|
log.error("录像异常:{}", ex.getMessage());
|
}
|
return url;
|
}
|
|
/**
|
* 引导目标位置
|
* 刘苏义
|
* 2023/10/17 8:27:48
|
*/
|
@Override
|
public Boolean guideTargetPosition(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
try {
|
ArdCamera ardCamera = redisService.getCacheMapValue(CacheConstants.CAMERA_LIST, cameraId);
|
if (StringUtils.isNull(ardCamera)) {
|
log.warn("相机不存在:{}", cameraId);
|
return false;
|
}
|
Point cameraPosition = new Point(ardCamera.getLongitude(), ardCamera.getLatitude(),
|
ardCamera.getAltitude());
|
cameraPosition.validate(); // 校验目标位置
|
Point targetPositions = cmd.getTargetPosition();
|
targetPositions.validate(); // 校验目标位置
|
PtzDto cameraPTZ = GisUtil.getCameraPTZ(cameraPosition, targetPositions, 20, 150);
|
//修正俯仰
|
double correctPitch = correctPitch(cmd);
|
int p = (int) (cameraPTZ.getP() * 10);
|
int t = (int) ((cameraPTZ.getT() + correctPitch) * 10);
|
t = (t > 900) ? (3600 - t) : (-t);
|
int z = (int) (cameraPTZ.getZ().floatValue());
|
boolean bool = netsdk.CLIENT_DHPTZControlEx(loginId, chanNo - 1,
|
NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_EXACTGOTO, p, t, z, 0);
|
if (!bool) {
|
String errorMsg = String.format("设置ptz失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return false;
|
}
|
return true;
|
} catch (Exception ex) {
|
log.error("引导目标位置异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
|
//转至预置点
|
@Override
|
public Boolean gotoPreset(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
Integer presetIndex = cmd.getPresetIndex();
|
NetSDKLib.LLong loginId = new NetSDKLib.LLong(cmd.getLoginId());
|
|
try {
|
boolean success = netsdk.CLIENT_DHPTZControlEx(
|
loginId, chanNo - 1, NET_PTZ_POINT_MOVE_CONTROL, 0, presetIndex, 0, 0
|
);
|
|
if (!success) {
|
log.warn("【{}】转至预置点失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
log.info("【{}】转至预置点成功: {}", cameraId, presetIndex);
|
return true;
|
} catch (Exception ex) {
|
log.error("【{}】转至预置点异常: {}", cameraId, ex.getMessage(), ex);
|
return false;
|
}
|
}
|
|
|
// 设置预置位
|
@Override
|
public Boolean setPreset(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
Integer presetIndex = cmd.getPresetIndex();
|
NetSDKLib.LLong loginId = new NetSDKLib.LLong(cmd.getLoginId());
|
|
try {
|
boolean success = netsdk.CLIENT_DHPTZControlEx(loginId, chanNo - 1, NET_PTZ_POINT_SET_CONTROL,
|
0, presetIndex, 0, 0);
|
|
if (!success) {
|
log.warn("【{}】设置预置点失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
log.info("【{}】设置预置点成功: {}", cameraId, presetIndex);
|
return true;
|
} catch (Exception ex) {
|
log.error("【{}】设置预置点异常: {}", cameraId, ex.getMessage(), ex);
|
return false;
|
}
|
}
|
|
|
//切换聚焦模式
|
@Override
|
public Boolean controlFocusMode(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
boolean enable = cmd.isEnable();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
|
CFG_VIDEO_IN_FOCUS cfg_video_in_focus = new CFG_VIDEO_IN_FOCUS();
|
cfg_video_in_focus.nVideoInFocusRealNum = 3; // 配置使用个数
|
for (int i = 0; i < 3; i++) {
|
cfg_video_in_focus.stVideoInFocusUnit[i].nSensitivity = 1; // 聚焦灵敏度, 0-高, 1-默认, 2-低
|
cfg_video_in_focus.stVideoInFocusUnit[i].nIRCorrection = 2; // 红外光聚焦修正, 0-不修正, 1-修正, 2-自动修正
|
cfg_video_in_focus.stVideoInFocusUnit[i].nFocusLimit = 10000; // 聚焦极限值, 单位毫米
|
|
if (enable) { // 手动聚焦
|
cfg_video_in_focus.stVideoInFocusUnit[i].nMode = 4;
|
cfg_video_in_focus.stVideoInFocusUnit[i].emFocusMode = 0;
|
log.debug("当前为手动聚焦模式");
|
} else { // 自动聚焦
|
cfg_video_in_focus.stVideoInFocusUnit[i].nMode = 2;
|
cfg_video_in_focus.stVideoInFocusUnit[i].emFocusMode = 1;
|
log.debug("当前为自动聚焦模式");
|
}
|
}
|
cfg_video_in_focus.nChannelIndex = chanNo - 1;
|
|
boolean bool = ConfigModule.SetDevConfig(loginId, chanNo - 1, NetSDKLib.CFG_CMD_VIDEOIN_FOCUS,
|
cfg_video_in_focus);
|
|
if (!bool) {
|
String errorMsg = String.format("切换聚焦模式失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return false; // 或者直接抛异常,视你统一风格
|
}
|
|
log.debug("切换聚焦模式成功");
|
return true;
|
}
|
|
|
//获取聚焦模式
|
@Override
|
public Boolean getFocusMode(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
|
NET_VIDEOIN_FOCUSMODE_INFO focusModeInfo = new NET_VIDEOIN_FOCUSMODE_INFO();
|
int emCfgOpType = NET_EM_CFG_OPERATE_TYPE.NET_EM_CFG_VIDEOIN_FOCUSMODE;
|
|
boolean success = ConfigModule.GetConfig(loginId, chanNo - 1, emCfgOpType, focusModeInfo);
|
if (!success) {
|
String errorMsg = String.format("获取聚焦模式失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return false;
|
}
|
|
// 判断手动/自动
|
// 0=关闭, 1=辅助, 2=自动, 3=半自动, 4=手动
|
return focusModeInfo.emFocusMode == 4; // true=手动聚焦, false=自动聚焦
|
}
|
|
|
@Override
|
public Boolean controlPTHeater(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
@Override
|
public Boolean controlCameraDeicing(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
@Override
|
public Boolean getPTZLockInfo(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
@Override
|
public Boolean setPTZLockInfo(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
// 切换透雾
|
@Override
|
public Boolean controlDefog(CameraCmd cmd) {
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
try {
|
String command = EM_NEW_CONFIG.CFG_CMD_VIDEOINDEFOG.getValue();
|
NET_VIDEOIN_DEFOG_INFO defogInfo = new NET_VIDEOIN_DEFOG_INFO();
|
boolean success = ConfigModule.SetDevConfig(loginId, chanNo - 1, command, defogInfo);
|
if (!success) {
|
log.warn("【{}】切换透雾失败: {}", cmd.getCameraId(), getErrorCodePrint());
|
}
|
return success;
|
} catch (Exception ex) {
|
log.error("切换透雾异常:{}", ex.getMessage());
|
return false;
|
}
|
}
|
|
//查询是否开启透雾
|
@Override
|
public Boolean isEnableDefog(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
try {
|
String command = EM_NEW_CONFIG.CFG_CMD_VIDEOINDEFOG.getValue();
|
NET_VIDEOIN_DEFOG_INFO netVideoinDefogInfo = new NET_VIDEOIN_DEFOG_INFO();
|
boolean success = ConfigModule.GetNewDevConfig(loginId, chanNo - 1, command, netVideoinDefogInfo);
|
if (!success) {
|
log.warn("查询透雾失败: {}", getErrorCodePrint());
|
return false;
|
}
|
return netVideoinDefogInfo.bCamDefogEnable;
|
} catch (Exception ex) {
|
log.error("查询透雾异常: {}", ex.getMessage());
|
return false;
|
}
|
}
|
|
|
//切换红外
|
@Override
|
public Boolean controlInfrared(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
|
try {
|
CFG_INFRARED_INFO cfgInfraredInfo = new CFG_INFRARED_INFO();
|
boolean success = ConfigModule.SetDevConfig(loginId, chanNo - 1, CFG_CMD_INFRARED_CONFIG, cfgInfraredInfo);
|
|
if (!success) {
|
log.warn("【{}】切换红外失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
log.info("【{}】切换红外成功", cameraId);
|
return true;
|
|
} catch (Exception e) {
|
log.error("【{}】切换红外异常: {}", cameraId, e.getMessage());
|
return false;
|
}
|
}
|
|
|
//查询是否开启红外
|
// 查询是否开启红外
|
@Override
|
public Boolean isEnableInfrared(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
try {
|
CFG_INFRARED_INFO cfgInfraredInfo = new CFG_INFRARED_INFO();
|
boolean success = ConfigModule.GetDevConfig(loginId, chanNo - 1, CFG_CMD_INFRARED_CONFIG, cfgInfraredInfo);
|
if (!success) {
|
log.warn("【{}】查询是否开启红外失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
|
// emInfraredMode 可以根据设备文档判断 0/1 表示关闭/开启
|
return cfgInfraredInfo.emInfraredMode != 0;
|
} catch (Exception ex) {
|
log.error("【{}】查询是否开启红外异常", cameraId, ex);
|
return false;
|
}
|
}
|
|
|
//获取聚焦值
|
@Override
|
public Integer getFocusPos(CameraCmd cmd) {
|
try {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new NetSDKLib.LLong(cmd.getLoginId());
|
|
NET_PTZ_LOCATION_INFO net_ptz_location_info = new NET_PTZ_LOCATION_INFO();
|
net_ptz_location_info.nChannelID = chanNo - 1;
|
|
boolean success = ConfigModule.queryDevState(loginId, NET_DEVSTATE_PTZ_LOCATION, net_ptz_location_info);
|
if (!success) {
|
log.warn("获取聚焦值失败,{}", getErrorCodePrint());
|
return null; // 返回 null 表示失败,Controller 层处理
|
}
|
|
return net_ptz_location_info.nFocusMapValue;
|
} catch (Exception ex) {
|
log.error("获取聚焦值异常: {}", ex.getMessage());
|
return null; // 返回 null 表示失败
|
}
|
}
|
|
|
/**
|
* 设置聚焦值(大华实现)
|
*/
|
@Override
|
public Boolean setFocusPos(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
boolean enable = cmd.isEnable();
|
NetSDKLib.LLong loginId = new NetSDKLib.LLong(cmd.getLoginId());
|
|
try {
|
CFG_LIGHTING_INFO cfg = new CFG_LIGHTING_INFO();
|
cfg.nLightingDetailNum = 1;
|
// 开启/关闭聚焦模式
|
cfg.stuLightingDetail[0].emMode = enable
|
? EM_CFG_LIGHTING_MODE.EM_CFG_LIGHTING_MODE_MANUAL
|
: EM_CFG_LIGHTING_MODE.EM_CFG_LIGHTING_MODE_OFF;
|
|
boolean success = ConfigModule.SetDevConfig(loginId, chanNo - 1, CFG_CMD_LIGHTING, cfg);
|
if (!success) {
|
log.warn("【{}】设置聚焦值失败: {}", cameraId, getErrorCodePrint());
|
return false;
|
}
|
return true;
|
} catch (Exception e) {
|
log.error("【{}】设置聚焦值异常: {}", cameraId, e.getMessage(), e);
|
return false;
|
}
|
}
|
|
|
//获取码流压缩参数
|
@Override
|
public VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd) {
|
String cameraId = cmd.getCameraId();
|
Integer chanNo = cmd.getChanNo();
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
CFG_ENCODE_INFO cfg_encode_info = new CFG_ENCODE_INFO();
|
cfg_encode_info.nChannelID = chanNo - 1;
|
boolean b = ConfigModule.GetNewDevConfig(loginId, chanNo - 1, CFG_CMD_ENCODE, cfg_encode_info);
|
if (!b) {
|
String errorMsg = String.format("获取码流压缩参数失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return null;
|
}
|
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;
|
|
}
|
|
//获取云台可视域
|
@Override
|
public PtzParamDTO getGisInfo(CameraCmd cmd) {
|
try {
|
NetSDKLib.LLong loginId = new LLong(cmd.getLoginId());
|
DH_OUT_PTZ_VIEW_RANGE_STATUS dh_out_ptz_view_range_status = new DH_OUT_PTZ_VIEW_RANGE_STATUS();
|
boolean b = ConfigModule.queryRemotDevState(loginId, 0, NET_DEVSTATE_PTZ_VIEW_RANGE,
|
dh_out_ptz_view_range_status);
|
if (!b) {
|
String errorMsg = String.format("获取云台可视域失败,%s", getErrorCodePrint());
|
log.warn(errorMsg);
|
return new PtzParamDTO();
|
}
|
float nAngelH = (float) dh_out_ptz_view_range_status.nAngelH / 10;
|
float nAngelV = (float) dh_out_ptz_view_range_status.nAngelV / 10;
|
PtzParamDTO ptzParamDTO = new PtzParamDTO();
|
PtzDto ptzDto = getPtz(cmd);
|
if (ptzDto != null) {
|
ptzParamDTO.setP(ptzDto.getP());
|
ptzParamDTO.setT(ptzDto.getT());
|
ptzParamDTO.setZ(ptzDto.getZ());
|
} else {
|
ptzParamDTO.setP(0f);
|
ptzParamDTO.setT(0f);
|
ptzParamDTO.setZ(0f);
|
}
|
ptzParamDTO.setFHorFieldAngle(nAngelH);
|
ptzParamDTO.setFVerFieldAngle(nAngelV);
|
return ptzParamDTO;
|
} catch (Exception ex) {
|
log.error("获取云台可视域异常:{}", ex.getMessage());
|
return new PtzParamDTO();
|
}
|
}
|
|
//3d定位
|
@Override
|
public Boolean set3DPosition(CameraCmd cmd) {
|
throw new CameraSDKException(
|
CameraSDKException.ErrorCode.CMD_NOT_SUPPORT.getCode(),
|
cmd.getCameraId(),
|
"功能暂不支持"
|
);
|
}
|
|
@Override
|
public DeviceCFG getSystemInfo(CameraCmd cmd) {
|
return null;
|
}
|
|
// 设备断线回调: 当设备出现断线时,SDK会调用该函数
|
private static class DisConnect implements NetSDKLib.fDisConnect {
|
@Override
|
public void invoke(LLong lLoginID, String pchDVRIP, int nDVRPort, Pointer dwUser) {
|
log.warn("Device[{}:{}] DisConnect!", pchDVRIP, nDVRPort);
|
}
|
}
|
|
// 网络连接恢复回调:设备重连成功回调,当已断线的设备重连成功时,SDK会调用该函数
|
private static class HaveReConnect implements NetSDKLib.fHaveReConnect {
|
@Override
|
public void invoke(LLong lLoginID, String pchDVRIP, int nDVRPort, Pointer dwUser) {
|
log.warn("ReConnect Device[{}:{}]", pchDVRIP, nDVRPort);
|
}
|
}
|
|
// 抓图接收回调:当抓图成功,sdk会调用该函数
|
public class fCaptureReceiveCB implements NetSDKLib.fSnapRev {
|
private final String objectName;
|
private final String bucketName;
|
private final String type;
|
private final CompletableFuture<String> urlFuture; // 用于存储URL的 CompletableFuture
|
|
public fCaptureReceiveCB(String bucketName, String objectName, String type,
|
CompletableFuture<String> urlFuture) {
|
this.bucketName = bucketName;
|
this.objectName = objectName;
|
this.type = type;
|
this.urlFuture = urlFuture;
|
}
|
|
@Override
|
public void invoke(LLong lLoginID, Pointer pBuf, int RevLen, int EncodeType, int CmdSerial, Pointer dwUser) {
|
if (pBuf != null && RevLen > 0) {
|
byte[] buf = pBuf.getByteArray(0, RevLen);
|
//存储到minio
|
InputStream inputStream = new ByteArrayInputStream(buf);
|
MultipartFile multipartFile = MultipartFileUtil.inputStreamToMultipartFile(inputStream, objectName,
|
MimeTypeUtils.IMAGE_JPEG);
|
R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
|
if (sysFileR.getCode() == R.SUCCESS) {
|
String url = sysFileR.getData().getUrl();
|
log.info("上传文件成功:{}", url);
|
// 在回调中完成 CompletableFuture
|
urlFuture.complete(url);
|
}
|
}
|
}
|
}
|
|
public static class fCaptureReceiveBytes implements NetSDKLib.fSnapRev {
|
|
private final CompletableFuture<byte[]> imageFuture;
|
|
public fCaptureReceiveBytes(CompletableFuture<byte[]> imageFuture) {
|
this.imageFuture = imageFuture;
|
}
|
|
@Override
|
public void invoke(LLong lLoginID, Pointer pBuf, int RevLen,
|
int EncodeType, int CmdSerial, Pointer dwUser) {
|
|
if (pBuf == null || RevLen <= 0) {
|
return;
|
}
|
|
byte[] imageBytes = pBuf.getByteArray(0, RevLen);
|
imageFuture.complete(imageBytes);
|
}
|
}
|
|
/**
|
* 动态库加载 - 大华 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;
|
}
|
@Override
|
public List<Map<String, Object>> getCameraVideoLable(Long userId, CameraCmd cmd)throws ServiceException{
|
return null;
|
}
|
}
|