大华摄像头windows、linuxJavaSDK开发使用
文章目录
简介
本文档主要介绍 SDK 接口参考信息,包括主要功能、接口函数和回调函数。
主要功能包括:SDK 初始化、设备登录、实时预览、云台控制、语音对讲、报警监听、智能订阅、
录像回放和录像下载等。
根据环境不同,开发包包含的文件会不同,具体如下所示。
Windows 开发包所包含的文件如下:
Linux 开发包所包含的文件如下:
SDK 的功能库和配置库是必备库。
功能库是设备网络 SDK 的主体,主要用于网络客户端与各类产品之间的通讯交互,负责远程
控制、查询、配置及码流数据的获取和处理等。
配置库针对配置功能的结构体进行打包和解析。
推荐使用播放库进行码流解析和播放。
环境要求
1、推荐内存:不低于 512 MB。
2、Jdk 使用版本:jdk1.6;jdk1.8
3、SDK 支持的系统:
- Windows 10/Windows 8.1/Windows 7/vista/XP/2000 以及 Windows Server 2008/2003。
- Linux Red Hat/SUSE 等通用 Linux 系统
库加载问题及解决方法
目前提供 Windows(.dll)、Linux(.so)两种平台的动态库。其中 win 和 linux 分为 64 位、32 位版本。
而调用 C++动态库的主要的方式分为“直接使用 Java 项目”和“将 Java 项目作为其他项目的 jar
包依赖运行”两种。在加载库的过程中会出现“找不到动态库”的相关错误。
“找不到动态库”问题的根本原因为代码路径和物理路径不匹配。因为 linux 版本的动态库名称
相较于 Windows 版本多出 lib 前缀,故 Linux 环境下加载动态库需要注意 lib 的前缀,在拼接动态
库路径时需要加上 lib 前缀。使用 java.io.tmpdir 方式实现路径映射时,需要注意此种方式优先级
较低。
大华摄像头Java SDK,完成摄像头设备登录、视频录像
下载好项目部署所在服务器类型的sdk,如果windows 与linux 都要部署或使用可以将两个版本的sdk都下载下来,然后共用一套java代码,只需要判断当前系统类型然后加载不同的libs就好了。
目录结构
doc是sdk的文档存放路径
libs是c++依赖库
res是国际化相关配置
windows 的c++代码
Linux的C++代码
项目结构
Common包与lib包为大华sdk封装的代码,直接copy过来就好
libs包下有一个jnajar包,这个jar包可以通过maven命令打入本地厂库或者直接引入
按照demo的封装,进行自己的封装,以下是api包下的代码
登录
代码是否可用可直接运行main方法
import com.netsdk.common.Res;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.ToolKits;
import com.sun.jna.Pointer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Vector;
/**
* 设备登录
*/
@Slf4j
@Data
public class DeviceLoginControl {
public static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
public NetSDKLib configsdk = NetSDKLib.CONFIG_INSTANCE;
public NetSDKLib.LLong m_hPlayHandleOne = new NetSDKLib.LLong(0);
private Vector<String> chnlist = new Vector<String>();
public NetSDKLib.LLong m_hPlayHandleTwo = new NetSDKLib.LLong(0);
// 设备信息
public NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
private NetSDKLib.LLong lRealHandle;
private String videoStoragePath;
private Long cycleTime;
private Long cleanCycle;
// 登陆句柄
public NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0);
private static boolean bInit = false;
private static boolean bLogopen = false;
public DeviceLoginControl() {
}
public DeviceLoginControl(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect fHaveReConnect) {
init(disConnect, fHaveReConnect);
}
public boolean init(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) {
bInit = netsdk.CLIENT_Init(disConnect, null);
if (!bInit) {
log.info("Initialize SDK failed");
return false;
}
//打开日志,可选
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) {
log.error("Failed to open NetSDK log");
}
// 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK内部会自动进行重连操作
// 此操作为可选操作,但建议用户进行设置
netsdk.CLIENT_SetAutoReconnect(haveReConnect, null);
//设置登录超时时间和尝试次数,可选
int waitTime = 5000; //登录请求响应超时时间设置为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 = 10000; // 登录时尝试建立链接的超时时间
netParam.nGetConnInfoTime = 3000; // 设置子连接的超时时间
netParam.nGetDevInfoTime = 3000;//获取设备信息超时时间,为0默认1000ms
netsdk.CLIENT_SetNetworkParam(netParam);
return true;
}
public static void main(String[] args) throws IOException {
DeviceLoginControl deviceLoginControl = new DeviceLoginControl();
deviceLoginControl.login("ip", 37777, "username", "password");
}
public boolean login(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) {
//IntByReference nError = new IntByReference(0);
//入参
NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
pstInParam.nPort = m_nPort;
pstInParam.szIP = m_strIp.getBytes();
pstInParam.szPassword = m_strPassword.getBytes();
pstInParam.szUserName = m_strUser.getBytes();
//出参
NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
pstOutParam.stuDeviceInfo = m_stDeviceInfo;
//m_hLoginHandle = netsdk.CLIENT_LoginEx2(m_strIp, m_nPort, m_strUser, m_strPassword, 0, null, m_stDeviceInfo, nError);
m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
if (m_hLoginHandle.longValue() == 0) {
System.err.printf("Login Device[%s] Port[%d]Failed. %s\n", m_strIp, m_nPort, ToolKits.getErrorCodePrint());
} else {
System.out.println("Login Success [ " + m_strIp + " ]");
}
return m_hLoginHandle.longValue() == 0 ? false : true;
}
public void realPlayByDataType() {
// 创建Stin对象 设置码流格式 通道号 预览类型 用户数据 保存文件名称
NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE stIn = new NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE();
stIn.emDataType = NetSDKLib.EM_REAL_DATA_TYPE.EM_REAL_DATA_TYPE_GBPS;
stIn.nChannelID = 0;
stIn.rType = NetSDKLib.NET_RealPlayType.NET_RType_Realplay;
stIn.dwUser = null;
stIn.szSaveFileName = "D:\\DahuaResult\\result.flv"; // 转换后的裸H264码流文件名
// 创建Stout对象
NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE stOut = new NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE();
// 预览转码流保存
NetSDKLib.LLong lRealHandle = netsdk.CLIENT_RealPlayByDataType(m_hLoginHandle, stIn, stOut, 5000);
if (lRealHandle.longValue() != 0) {
System.out.println("RealPlayByDataType Succeed!");
} else {
System.err.printf("RealPlayByDataType Failed!Last Error[0x%x]\n", netsdk.CLIENT_GetLastError());
return;
}
try {
// 录像时间 单位毫秒
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 停止预览
netsdk.CLIENT_StopRealPlay(lRealHandle); // 必须停止拉流后,才会生成文件
}
public boolean logout() {
netsdk.CLIENT_StopRealPlayEx(this.m_hPlayHandleOne);
netsdk.CLIENT_StopRealPlayEx(this.m_hPlayHandleTwo);
if (m_hLoginHandle.longValue() == 0) {
return false;
}
boolean bRet = netsdk.CLIENT_Logout(m_hLoginHandle);
if (bRet) {
m_hLoginHandle.setValue(0);
}
for (int i = 0; i < m_stDeviceInfo.byChanNum; i++) {
chnlist.clear();
}
return true;
}
}
云台控制
import com.netsdk.lib.NetSDKLib;
public class PtzControl {
public static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
public static boolean ptzControlStart(NetSDKLib.LLong m_hLoginHandle, Integer NET_PTZ_ControlType) {
return netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, 0, NET_PTZ_ControlType, 2, 4, 0, 0);
}
public static boolean ptzControlEnd(NetSDKLib.LLong m_hLoginHandle, Integer NET_PTZ_ControlType) {
return netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, 0, NET_PTZ_ControlType, 0, 0, 0, 1);
}
/**
* 向上
*/
public static boolean ptzControlUpEnd(NetSDKLib.LLong m_hLoginHandle) {
return netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, 0, NetSDKLib.NET_PTZ_ControlType.NET_PTZ_UP_CONTROL, 0, 0, 0, 1);
}
/**
* 向下
*/
public static boolean ptzControlDownEnd(NetSDKLib.LLong m_hLoginHandle) {
return netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, 0, NetSDKLib.NET_PTZ_ControlType.NET_PTZ_DOWN_CONTROL, 0, 0, 0, 1);
}
/**
* 向左
*/
public static boolean ptzControlLeftEnd(NetSDKLib.LLong m_hLoginHandle) {
return netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, 0, NetSDKLib.NET_PTZ_ControlType.NET_PTZ_LEFT_CONTROL, 0, 0, 0, 1);
}
/**
* 向右
*/
public static boolean ptzControlRightEnd(NetSDKLib.LLong m_hLoginHandle) {
return netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, 0, NetSDKLib.NET_PTZ_ControlType.NET_PTZ_RIGHT_CONTROL, 0, 0, 0, 1);
}
public static boolean ptzControl(NetSDKLib.LLong m_hLoginHandle, Integer NET_PTZ_ControlType) {
boolean b = netsdk.CLIENT_DHPTZControlEx(m_hLoginHandle, 0, NET_PTZ_ControlType, 0, 0, 0, 1);
return b;
}
}
录像
import com.netsdk.lib.NetSDKLib;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Slf4j
public class VideoControl {
public static void startRealPlay() throws FileNotFoundException {
DeviceLoginControl deviceLoginControl = new DeviceLoginControl();
deviceLoginControl.login("192.168.0.108", 37777, "admin", "admin123");
NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE stIn = new NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE();
stIn.emDataType = NetSDKLib.EM_REAL_DATA_TYPE.EM_REAL_DATA_TYPE_GBPS;
stIn.nChannelID = 0;
stIn.rType = NetSDKLib.NET_RealPlayType.NET_RType_Realplay;
File xfile = new File("a.dav");
FileOutputStream fos = new FileOutputStream(xfile);
stIn.cbRealData = new NetSDKLib.fRealDataCallBackEx() {
@Override
public void invoke(NetSDKLib.LLong lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int param, Pointer dwUser) {
// 指定回调流为PS格式
if (dwDataType == 1001) {
byte[] byteArray = pBuffer.getByteArray(0, dwBufSize);
try {
fos.write(byteArray);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
};
stIn.dwUser = null;
stIn.szSaveFileName = "D:\\result.flv"; // 转换后的裸H264码流文件名
// 创建Stout对象
NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE stOut = new NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE();
// 预览转码流保存
NetSDKLib.LLong lRealHandle = deviceLoginControl.netsdk.CLIENT_RealPlayByDataType(deviceLoginControl.m_hLoginHandle, stIn, stOut, 5000);
if (lRealHandle.longValue() != 0) {
System.out.println("RealPlayByDataType Succeed!");
} else {
System.err.printf("RealPlayByDataType Failed!Last Error[0x%x]\n", deviceLoginControl.netsdk.CLIENT_GetLastError());
}
}
public static void videoRecording(DeviceLoginControl deviceLoginControl) {
NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE stIn = new NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE();
stIn.emDataType = NetSDKLib.EM_REAL_DATA_TYPE.EM_REAL_DATA_TYPE_GBPS;
stIn.nChannelID = 0;
stIn.rType = NetSDKLib.NET_RealPlayType.NET_RType_Realplay;
stIn.dwUser = null;
log.info("设备登录信息:{}", deviceLoginControl);
stIn.szSaveFileName = deviceLoginControl.getVideoStoragePath() + new SimpleDateFormat("YYYYMMddHHmmssSSS").format(new Date()) + ".flv"; // 转换后的裸H264码流文件名
// 创建Stout对象
NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE stOut = new NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE();
// 预览转码流保存
NetSDKLib.LLong lRealHandle = deviceLoginControl.netsdk.CLIENT_RealPlayByDataType(deviceLoginControl.m_hLoginHandle, stIn, stOut, 5000);
if (lRealHandle.longValue() != 0) {
// System.out.println(Res.string().getAttach());
System.out.println("RealPlayByDataType Succeed!");
} else {
throw new RuntimeException("录像失败.{}");
}
try {
// 录像时间 单位毫秒
Thread.sleep(deviceLoginControl.getCycleTime());
} catch (InterruptedException e) {
e.printStackTrace();
}
// 停止预览
deviceLoginControl.netsdk.CLIENT_StopRealPlay(lRealHandle); // 必须停止拉流后,才会生成文件
}
public static void videoRecording(Long cycleTime, String savePath, DeviceLoginControl deviceLoginControl) {
NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE stIn = new NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE();
stIn.emDataType = NetSDKLib.EM_REAL_DATA_TYPE.EM_REAL_DATA_TYPE_GBPS;
stIn.nChannelID = 0;
stIn.rType = NetSDKLib.NET_RealPlayType.NET_RType_Realplay;
stIn.dwUser = null;
stIn.szSaveFileName = savePath + new SimpleDateFormat("YYYYMMddHHmmssSSS").format(new Date()) + ".flv"; // 转换后的裸H264码流文件名
// 创建Stout对象
NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE stOut = new NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE();
// 预览转码流保存
NetSDKLib.LLong lRealHandle = deviceLoginControl.netsdk.CLIENT_RealPlayByDataType(deviceLoginControl.m_hLoginHandle, stIn, stOut, 5000);
if (lRealHandle.longValue() != 0) {
System.out.println("RealPlayByDataType Succeed!");
} else {
throw new RuntimeException("录像失败.{}");
}
try {
// 录像时间 单位毫秒
Thread.sleep(cycleTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 停止预览
deviceLoginControl.netsdk.CLIENT_StopRealPlay(lRealHandle); // 必须停止拉流后,才会生成文件
}
public static void main(String[] args) throws FileNotFoundException {
DeviceLoginControl deviceLoginControl = new DeviceLoginControl();
deviceLoginControl.login("ip", 37777, "username", "password");
videoRecording(300000l, "", deviceLoginControl);
}
}
DeviceContext
import com.netsdk.api.DeviceLoginControl;
import com.netsdk.common.Res;
import com.netsdk.common.SavePath;
import com.netsdk.lib.NetSDKLib;
import com.sun.jna.Pointer;
import com.web.device.manage.entity.CameraDeviceEntity;
import com.web.device.manage.service.CameraDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Slf4j
@Component
public class DeviceContext {
@Resource
private CameraDeviceService cameraDeviceService;
private Map<String, DeviceLoginControl> deviceLogin = new ConcurrentHashMap<>();
/**
* 初始化登录与重连
*/
@PostConstruct
private void init() {
List<CameraDeviceEntity> list = cameraDeviceService.list();
Map<String, List<CameraDeviceEntity>> collect = list.stream().collect(Collectors.groupingBy(CameraDeviceEntity::getIp));
for (CameraDeviceEntity cameraDevice : list) {
DeviceLoginControl deviceLoginControl = new DeviceLoginControl(new DisConnect(), new HaveReConnect());
if (loginRetry(cameraDevice, deviceLoginControl)) {
deviceLoginControl.setCycleTime(cameraDevice.getCycleTime());
deviceLoginControl.setVideoStoragePath(cameraDevice.getVideoStoragePath());
deviceLoginControl.setCleanCycle(cameraDevice.getCleanCycle());
deviceLogin.put(cameraDevice.getIp(), deviceLoginControl);
}
}
}
private boolean loginRetry(CameraDeviceEntity cameraDevice, DeviceLoginControl deviceLoginControl) {
boolean login = deviceLoginControl.login(cameraDevice.getIp(), cameraDevice.getTcpPort(), cameraDevice.getLoginName(), cameraDevice.getPassword());
if (!login) {
log.error("{},正在尝试重连", cameraDevice.getIp());
int count = 0;
while (!deviceLoginControl.login(cameraDevice.getIp(), cameraDevice.getTcpPort(), cameraDevice.getLoginName(), cameraDevice.getPassword())) {
count++;
log.error("重连 第{}次,请检查网络或设备是否在线.{}", count, cameraDevice.getIp());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
log.error("重连等待异常.{}", cameraDevice.getIp());
e.printStackTrace();
}
if (count >= 5) {
log.error("长时间重连失败,取消重连,请检查网络或设备是否在线.{}", cameraDevice.getIp());
return false;
}
}
log.info("重连成功.{}", cameraDevice.getIp());
}
return true;
}
@Scheduled(fixedDelay = 300000)
public void initDevice() {
List<CameraDeviceEntity> list = cameraDeviceService.list();
list.forEach(f -> {
if (!deviceLogin.containsKey(f.getIp()) && !deviceLogin.containsKey("DisConnect-" + f.getIp())) {
DeviceLoginControl deviceLoginControl = new DeviceLoginControl(new DisConnect(), new HaveReConnect());
if (loginRetry(f, deviceLoginControl)) {
deviceLoginControl.setCycleTime(f.getCycleTime());
deviceLoginControl.setVideoStoragePath(f.getVideoStoragePath());
deviceLoginControl.setCleanCycle(f.getCleanCycle());
deviceLogin.put(f.getIp(), deviceLoginControl);
}
}
});
}
public Map<String, DeviceLoginControl> getDeviceLogin() {
return deviceLogin;
}
public class fCaptureReceiveCB implements NetSDKLib.fSnapRev {
BufferedImage bufferedImage = null;
public void invoke(NetSDKLib.LLong lLoginID, Pointer pBuf, int RevLen, int EncodeType, int CmdSerial, Pointer dwUser) {
if (pBuf != null && RevLen > 0) {
String strFileName = SavePath.getSavePath().getSaveCapturePath();
System.out.println("strFileName = " + strFileName);
byte[] buf = pBuf.getByteArray(0, RevLen);
ByteArrayInputStream byteArrInput = new ByteArrayInputStream(buf);
try {
bufferedImage = ImageIO.read(byteArrInput);
if (bufferedImage == null) {
return;
}
ImageIO.write(bufferedImage, "jpg", new File(strFileName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class DisConnect implements NetSDKLib.fDisConnect {
/**
* 断线回调
*
* @param m_hLoginHandle
* @param pchDVRIP
* @param nDVRPort
* @param dwUser
*/
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("Device[%s] Port[%d] DisConnect!\n", pchDVRIP, nDVRPort);
if (deviceLogin.containsKey(pchDVRIP)) {
synchronized (deviceLogin) {
if (deviceLogin.containsKey(pchDVRIP)) {
DeviceLoginControl deviceLoginControl = deviceLogin.get(pchDVRIP);
deviceLogin.remove(pchDVRIP);
deviceLogin.put("DisConnect-" + pchDVRIP, deviceLoginControl);
}
}
}
Res.string().getDisConnectReconnecting();
}
}
private class HaveReConnect implements NetSDKLib.fHaveReConnect {
public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
System.out.printf("ReConnect Device[%s] Port[%d]\n", pchDVRIP, nDVRPort);
if (deviceLogin.containsKey("DisConnect-" + pchDVRIP)) {
synchronized (deviceLogin) {
if (deviceLogin.containsKey("DisConnect-" + pchDVRIP)) {
DeviceLoginControl deviceLoginControl = deviceLogin.get("DisConnect-" + pchDVRIP);
deviceLogin.remove("DisConnect-" + pchDVRIP);
deviceLogin.put(pchDVRIP, deviceLoginControl);
}
}
}
Res.string().getOnline();
}
}
}
调用的接口
package com.web.device.manage.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.netsdk.api.DeviceLoginControl;
import com.netsdk.api.PtzControl;
import com.netsdk.lib.NetSDKLib;
import com.web.common.constant.WebConstant;
import com.web.common.entity.web.Failure;
import com.web.common.entity.web.Result;
import com.web.common.utils.BeanUtils;
import com.web.common.web.BaseController;
import com.web.device.manage.DeviceContext;
import com.web.device.manage.entity.CameraDeviceEntity;
import com.web.device.manage.model.response.CameraDeviceResponseModel;
import com.web.device.manage.service.CameraDeviceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping(WebConstant.RequestMappingConstant.CAMERA_DEVICE)
public class CameraDeviceController extends BaseController {
@Resource
private CameraDeviceService cameraDeviceService;
@Resource
private RedisTemplate redisTemplate;
@Resource
private DeviceContext deviceContext;
@RequestMapping(WebConstant.RequestMappingConstant.LIST)
public Result list() {
return executor(e -> {
List<CameraDeviceEntity> cameraDevices = cameraDeviceService.list();
e.put("cameraDevices", BeanUtils.convertListToList(cameraDevices, new TypeReference<List<CameraDeviceResponseModel>>() {
}));
}, f -> Failure.instance(WebConstant.Code.ERROR, f.getMessage()));
}
@RequestMapping("/download")
public void download(@RequestBody Map<String, String> params, HttpServletResponse response) {
try {
// path是指想要下载的文件的路径
File file = new File(params.get("path"));
log.info(file.getPath());
// 获取文件名
String filename = file.getName();
// 获取文件后缀名
String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
log.info("文件后缀名:" + ext);
// 将文件写入输入流
FileInputStream fileInputStream = new FileInputStream(file);
InputStream fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
//Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
//attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(params.containsKey("name") ? params.get("name") : new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + "." + ext, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
@RequestMapping(WebConstant.RequestMappingConstant.SET_CLEAN_CYCLE)
public Result setCleanCycle(@PathVariable("cycle") Integer cycle) {
return executor(e -> {
redisTemplate.opsForValue().set("cleanCycle", cycle);
}, f -> Failure.instance(WebConstant.Code.ERROR, f.getMessage()));
}
@RequestMapping(WebConstant.RequestMappingConstant.GET_CLEAN_CYCLE)
public Result getCleanCycle() {
return executor(e -> {
Object cleanCycle = redisTemplate.opsForValue().get("cleanCycle");
if (cleanCycle == null) {
redisTemplate.opsForValue().set("cleanCycle", 7);
e.put("cycle", 7);
} else {
e.put("cycle", cleanCycle);
}
}, f -> Failure.instance(WebConstant.Code.ERROR, f.getMessage()));
}
@RequestMapping(WebConstant.RequestMappingConstant.PTZ_CONTROL)
public Result ptzControl(@RequestBody Map<String, Object> params) {
return executor(e -> {
Map<String, DeviceLoginControl> deviceLogin = deviceContext.getDeviceLogin();
DeviceLoginControl deviceLoginControl = deviceLogin.get(params.get("deviceIp"));
if (!PtzControl.ptzControlStart(deviceLoginControl.getM_hLoginHandle(), (Integer) params.get("type"))) {
throw new RuntimeException("控制失败");
}
Thread.sleep(500);
if (!PtzControl.ptzControlEnd(deviceLoginControl.getM_hLoginHandle(), (Integer) params.get("type"))) {
throw new RuntimeException("控制失败");
}
}, f -> Failure.instance(WebConstant.Code.ERROR, f.getMessage()));
}
}
注意
libs的加载地址要指定
码云地址
相关文章:

并发编程下的集合:数组寻址、LinkedList、HashMap、ConcurrentHashMap
如果发现hash取模后的数组索引位下无元素则直接新增,若不是空那就说明存在hash冲突,则判断数组索引位链表结构中的第一个元素的key以及hash值是否与新的key一致则直接覆盖,若不一致则判断当前的数组索引下的链表结构是否为红黑树,若为红黑树则走红黑树的新增方法,若不为红黑树则遍历当前链表结构,遍历中发现某个节点元素的next为null是则直接将新元素指针与next进行关联,若在遍历到next为空前判断到,某个节点的key以及key的hash值与新的key与新的keyhash值一致时则走覆盖。

【日常开发之插件篇】IDEA plugins 神器助我!!
今早因为老代码的一些bug让我突然觉得Idea的一些插件特别好用,我准备将我平时所用到的一些插件做个推荐以及记录。

【日常开发之FTP】Windows开启FTP、Java实现FTP文件上传下载
FTP是一个专门进行文件管理的操作服务,一般来讲可以在任意的操作系统之中进行配置,但是如果考虑到简便性,一般来讲可以直接在Linux系统下进行安装。FTP (File Transfer Protocol、文件传输协议)是TCP/IP协议中的一部分,属于应用层协议。使用FTP最主要的功能是对文件进行管理,所以在FTP内部对于文件支持有两种传输模式:文本模式(ASCII、默认)和二进制模式(Binary),通常文本文件使用ASCIl模式,而对于图片、视频、声音、压缩等文件则会使用二进制的方式进行传输。

【Linux之升华篇】Linux内核锁、用户模式与内核模式、用户进程通讯方式
alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。字符设备描述符 struct cdev,cdev_alloc()用于动态的分配 cdev 描述符,cdev_add()用于注。外,还支持语义符合 Posix.1 标准的信号函数 sigaction(实际上,该函数是基于 BSD 的,BSD。从最初的原子操作,到后来的信号量,从。(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的。

【Mongdb之数据同步篇】什么是Oplog、Mongodb 开启oplog,java监听oplog并写入关系型数据库、Mongodb动态切换数据源
oplog是local库下的一个固定集合,Secondary就是通过查看Primary 的oplog这个集合来进行复制的。每个节点都有oplog,记录这从主节点复制过来的信息,这样每个成员都可以作为同步源给其他节点。Oplog 可以说是Mongodb Replication的纽带了。

【日常开发之Windows共享文件】Java实现Windows共享文件上传下载
下拉框选择你选择的用户点击添加,然后共享确定。创建一个文件夹然后点击属性界面,点击共享。maven版本存在于SMB协议的兼容问题。首先开启服务,打开控制面板点击程序。点击启用或关闭Windows功能。我这边是专门创建了一个用户。SMB1.0选中红框内的。

CXFServlet类的作用
CXFServlet是Apache CXF框架中的一个核心组件,用于处理HTTP请求并将它们转换为Web服务调用。通过配置CXFServlet,你可以轻松地部署和管理SOAP和RESTful Web服务。

@Scheduled注解的scheduler属性什么作用
注解是 Spring Framework 提供的一种机制,用于定义计划任务,即周期性执行的任务。 注解可以应用于方法上,以指示 Spring 容器在特定的时间间隔或按照某种调度规则来调用该方法。 属性是 注解的一个可选属性,它的作用是允许开发者指定一个自定义的 对象来控制任务的调度方式。默认情况下, 注解使用 Spring 内部的 来执行任务,但如果需要更高级的定制化需求,可以通过 属性指定一个自定义的 实现。自定义调度器:共享调度器资源:高级调度需求:假设你想使用 作为调度器,并且希望所有带有

过滤器、拦截器、aop的先后顺序和作用范围&拦截器preHandle(),postHandle(),afterComplation()方法执行顺序
在Spring框架中,过滤器(Filter)、拦截器(Interceptor)和面向切面编程(AOP)都是用于处理请求和处理流程的组件,但它们的作用范围和触发时机有所不同。下面我会解释这三者的先后顺序和作用范围。执行顺序:请注意,这个顺序可能因具体的配置和使用的技术而有所不同。在实际应用中,建议根据项目的具体需求来合理配置和使用这些组件。拦截器执行流程图:实现拦截器需要实现这个接口,这个 接口中有三个默认方法,这三个方法的执行顺序:我们实现接口然后重写这三个方法,就会在对应的时机被自动执行。这里就是调用处理

Zookeeper概要、协议、应用场景
Zoopkeeper提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,作为分布式系统的沟通调度桥梁。

spring.factories文件的作用
即spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器中。在Spring Boot启动时,它会扫描classpath下所有的spring.factories文件,加载其中的自动配置类,并将它们注入到Spring ApplicationContext中,使得项目能够自动运行。spring.factories文件是Spring Boot自动配置的核心文件之一,它的作用是。

Spring事务七大传播机制与五个隔离级别,嵌套事务
如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。

常见的七种加密算法及实现
**数字签名**、**信息加密** 是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、`oauth` 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 **签名加密算法** 来达到业务目标。这里简单的给大家介绍几种常见的签名加密算法和一些典型场景下的应用。## 正文### 1. 数字签名**数字签名**,简单来说就是通过提供 **可鉴别** 的 **数字信息** 验证 **自身身份** 的一种方式。一套 **数字签名** 通常定义两种 **互补

7min到40s:SpringBoot 启动优化实践
然后重点排查这些阶段的代码。先看下。

SpringBoot系列教程之Bean之指定初始化顺序的若干姿势
之前介绍了@Order注解的常见错误理解,它并不能指定 bean 的加载顺序,那么问题来了,如果我需要指定 bean 的加载顺序,那应该怎么办呢?本文将介绍几种可行的方式来控制 bean 之间的加载顺序。

在Java中使用WebSocket
WebSocket是一种协议,用于在Web应用程序和服务器之间建立实时、双向的通信连接。它通过一个单一的TCP连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket协议最初由W3C开发,并于2011年成为标准。

3种方案,模拟两个线程抢票
在多线程编程中,资源竞争是一个常见的问题。资源竞争发生在多个线程试图同时访问或修改共享资源时,可能导致数据不一致或其他并发问题。在模拟两个线程抢票的场景中,我们需要考虑如何公平地分配票,并确保每个线程都有机会成功获取票。本篇文章将通过三种方式来模拟两个线程抢票的过程,以展示不同的并发控制策略。使用 Synchronized 来确保一次只有一个线程可以访问票资源。使用 ReentrantLock 来实现线程间的协调。使用 Semaphore 来限制同时访问票的线程数量。

替代Druid,HakariCP 为什么这么快?
这次源码探究,真的感觉看到了无数个小细节,无数个小优化,积少成多。平时开发过程中,一些小的细节也一定要“扣”。

Java中volatile 的使用场景有哪些?
volatile是一种轻量级的同步机制,它能保证共享变量的可见性,同时禁止重排序保证了操作的有序性,但是它无法保证原子性。所以使用volatilevolatile。

JDK22 正式发布了 !
Java 22 除了推出了新的增强功能和特性,也获得 Java Management Service (JMS) 的支持,这是一项新的 Oracle 云基础设施远程软件服务(Oracle Cloud Infrastructure, OCI) 原生服务,提供统一的控制台和仪表盘,帮助企业管理本地或云端的 Java 运行时和应用。使包含运行时计算值的字符串更容易表达,简化 Java 程序的开发工作,同时提高将用户提供的值编写成字符串,并将字符串传递给其他系统的程序的安全性。支持开发人员自由地表达构造器的行为。

Jackson 用起来!
你可以创建自定义序列化器和反序列化器以自定义特定字段或类的序列化和反序列化行为。为此,请创建一个实现或接口的类,并在需要自定义的字段或类上使用和注解。@Override// ...其他代码...优势性能优异:Jackson在序列化和反序列化过程中表现出优秀的性能,通常比其他Java JSON库更快。灵活性:通过注解、自定义序列化器/反序列化器等功能,Jackson提供了丰富的配置选项,允许你根据需求灵活地处理JSON数据。易于使用:Jackson的API设计简洁明了,易于学习和使用。

拜托!别再滥用 ! = null 判空了!!
另外,也许受此习惯影响,他们总潜意识地认为,所有的返回都是不可信任的,为了保护自己程序,就加了大量的判空。如果你养成习惯,都是这样写代码(返回空collections而不返回null),你调用自己写的方法时,就能大胆地忽略判空)这种情况下,null是个”看上去“合理的值,例如,我查询数据库,某个查询条件下,就是没有对应值,此时null算是表达了“空”的概念。最终,项目中会存在大量判空代码,多么丑陋繁冗!,而不要返回null,这样调用侧就能大胆地处理这个返回,例如调用侧拿到返回后,可以直接。

详解Java Math类的toDegrees()方法:将参数从弧度转换为角度
Java Math 类的 toDegrees() 方法是将一个角度的弧度表示转换为其度表示,返回值为double类型,表示从弧度数转换而来的角度数。这就是Java Math 类的 toDegrees() 方法的攻略。我们已经了解了该方法的基本概念、语法、注意事项以及两个示例。希望这篇攻略对你有所帮助。

SpringBoot接口防抖(防重复提交)的一些实现方案
作为一名老码农,在开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。啥是防抖所谓防抖,一是防用户手抖,二是防网络抖动。

公司新来一个同事:为什么 HashMap 不能一边遍历一边删除?一下子把我问懵了!
前段时间,同事在代码中KW扫描的时候出现这样一条:上面出现这样的原因是在使用foreach对HashMap进行遍历时,同时进行put赋值操作会有问题,异常ConcurrentModificationException。于是帮同简单的看了一下,印象中集合类在进行遍历时同时进行删除或者添加操作时需要谨慎,一般使用迭代器进行操作。于是告诉同事,应该使用迭代器Iterator来对集合元素进行操作。同事问我为什么?这一下子把我问蒙了?对啊,只是记得这样用不可以,但是好像自己从来没有细究过为什么?

每天一个摆脱if-else工程师的技巧——优雅的参数校验
在日常的开发工作中,为了程序的健壮性,大部分方法都需要进行入参数据校验。最直接的当然是在相应方法内对数据进行手动校验,但是这样代码里就会有很多冗余繁琐的if-else。throw new IllegalArgumentException("用户姓名不能为空");throw new IllegalArgumentException("性别不能为空");throw new IllegalArgumentException("性别错误");

SpringBoot请求转发与重定向
但是可能由于B网址相对于A网址过于复杂,这样搜索引擎就会觉得网址A对用户更加友好,因而在重定向之后任然显示旧的网址A,但是显示网址B的内容。在平常使用手机的过程当中,有时候会发现网页上会有浮动的窗口,或者访问的页面不是正常的页面,这就可能是运营商通过某种方式篡改了用户正常访问的页面。重定向,是指在Nginx中,重定向是指通过修改URL地址,将客户端的请求重定向到另一个URL地址的过程,Nginx中实现重定向的方式有多种,比如使用rewrite模块、return指令等。使用场景:在返回视图的前面加上。

SSO 单点登录和 OAuth2.0 有何区别?
此方法的缺点是它依赖于浏览器和会话状态,对于分布式或者微服务系统而言,可能需要在服务端做会话共享,但是服务端会话共享效率比较低,这不是一个好的方案。在单点登录的上下文中,OAuth 可以用作一个中介,用户在一个“授权服务器”上登录,并获得一个访问令牌,该令牌可以用于访问其他“资源服务器”上的资源。首先,SSO 主要关注用户在多个应用程序和服务之间的无缝切换和保持登录状态的问题。这种方法通过将登录认证和业务系统分离,使用独立的登录中心,实现了在登录中心登录后,所有相关的业务系统都能免登录访问资源。

TCP协议-TCP连接管理
TCP协议是 TCP/IP 协议族中一个非常重要的协议。它是一种面向连接、提供可靠服务、面向字节流的传输层通信协议。TCP(Transmission Control Protocol,传输控制协议)。

接口响应慢?那是你没用 CompletableFuture 来优化!
大多数程序员在平时工作中,都是增删改查。这里我跟大家讲解如何利用CompletableFuture优化项目代码,使项目性能更佳!