您的位置:首页 > 科技 > 能源 > 庆阳网红农村娃宝军_小型公司网络搭建_推广网站的方法_河南it渠道网

庆阳网红农村娃宝军_小型公司网络搭建_推广网站的方法_河南it渠道网

2024/12/23 11:05:50 来源:https://blog.csdn.net/qq_42882229/article/details/144351361  浏览:    关键词:庆阳网红农村娃宝军_小型公司网络搭建_推广网站的方法_河南it渠道网
庆阳网红农村娃宝军_小型公司网络搭建_推广网站的方法_河南it渠道网

一、背景及注意点

项目需要根据时间段从海康监控中获取对应的视频,我只参考文档做了这一个功能,其他功能可自行参考官方代码进行修改,重点讲下如何整合及踩坑

二、步骤

2.1、官网

首先要去官网下载对应的开发包,一般都是window和linux的,都直接下载下来就行。https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type=10
一般来说都是64位的,注意:这里是没有mac版本的
在这里插入图片描述

2.2、整合

2.2.1、jar包

首先将其中的jar包放到自己对应的resources目录下,有两个
在这里插入图片描述
然后pom中加上

       <dependency><groupId>examples</groupId><artifactId>examples</artifactId><version>1.0</version><scope>system</scope><systemPath>${basedir}/src/main/resources/lib/examples.jar</systemPath></dependency><dependency><groupId>jna</groupId><artifactId>jna</artifactId><version>1.0</version><scope>system</scope><systemPath>${basedir}/src/main/resources/lib/jna.jar</systemPath></dependency>

很重要的一点,打包插件一定要加上这个,否则jar包只会在resources中

<!--设置将本地jar包导出到项目最终的依赖库中--><includeSystemScope>true</includeSystemScope>

在这里插入图片描述

2.2.2、文件

我把window的文件都放在这里了,可自行随意放置
在这里插入图片描述

2.2.3、yaml配置文件

我自己加了几个配置参数

#监控一些资源的配置
video:monitor:# window中ffmpeg路径,linux不用管ffmpegpath:window: D:/workplaces/device-supervision/monitorvideo/ffmpeg.exe#监控需要加载的动态库,最后不用加斜杠load:window: D:/workplaces/jianshe/device-supervision/monitorvideolinux: /home/monitorvideo##监控连接相关配置
monitor:ip: # 这个默认就是8000,一般不用改port: 8000user: password: 

2.2.4、对应的Configuration文件啥的

MonitorProperties 读取监控相关配置

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** 读取监控相关配置*/
@Component
@ConfigurationProperties(prefix = "monitor")
@Data
public class MonitorProperties {/*** 监控ip地址*/private String ip;/*** 监控端口*/private short port;/*** 用户名*/private String user;/*** 用户密码*/private String password;
}

HikVisionConfiguration 用来初始化加载海康的一些类,这些东西给的实例代码中都有的,像OsSelectUtil,FExceptionCallBackException啥的

@Configuration
@Slf4j
public class HikVisionConfiguration {private HCNetSDK hCNetSDK = null;private FExceptionCallBackException fExceptionCallBack;@Value("${video.monitor.load.window}")private String windowPath;@Value("${video.monitor.load.linux}")private String linuxPath;/*** 播放库加载*/@Beanpublic PlayCtrl createPlayInstance() {PlayCtrl playControl;synchronized (PlayCtrl.class) {String strPlayPath = "";try {if (OsSelectUtil.isWindows()) {//win系统加载库路径strPlayPath = windowPath + "\\lib\\HCNetSDK.dll";} else if (OsSelectUtil.isLinux()) {//Linux系统加载库路径strPlayPath = linuxPath + "/lib/libPlayCtrl.so";}playControl = (PlayCtrl) Native.loadLibrary(strPlayPath, PlayCtrl.class);} catch (Exception ex) {log.error("加载失败,loadLibrary: {}", strPlayPath);log.error(ex.getMessage(), ex);return null;}}log.info("加载PlayCtrl成功");return playControl;}/*** 动态库加载*/@Beanpublic HCNetSDK init() {if (hCNetSDK == null) {synchronized (HCNetSDK.class) {String strDllPath = "";try {//win系统加载库路径if (OsSelectUtil.isWindows()) {strDllPath = windowPath + "\\lib\\HCNetSDK.dll";}//linux系统建议调用以下接口加载组件库else if (OsSelectUtil.isLinux()) {strDllPath = linuxPath + "/lib/libhcnetsdk.so";}hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);//linux系统建议调用以下接口加载组件库if (OsSelectUtil.isLinux()) {HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);//这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限String strPath1 = linuxPath + "/lib/libcrypto.so.1.1";String strPath2 = linuxPath + "/lib/libssl.so.1.1";log.info("strpaht1:{}",strPath1);log.info("strpaht2:{}",strPath2);System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());ptrByteArray1.write();hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_LIBEAY_PATH, ptrByteArray1.getPointer());System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());ptrByteArray2.write();hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_SSLEAY_PATH, ptrByteArray2.getPointer());String strPathCom = linuxPath + "/lib/";HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());struComPath.write();hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_SDK_PATH, struComPath.getPointer());}} catch (Exception ex) {log.error("加载失败,loadLibrary: {}" ,strDllPath);log.error(ex.getMessage(), ex);return null;}}}//SDK初始化,一个程序只需要调用一次hCNetSDK.NET_DVR_Init();//异常消息回调if (fExceptionCallBack == null) {fExceptionCallBack = new FExceptionCallBackException();}Pointer pUser = null;if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, pUser)) {log.error("设置异常消息回调失败");return null;}//启动SDK写日志hCNetSDK.NET_DVR_SetLogToFile(3, "./sdkLog", false);log.info("加载hksdk成功");return hCNetSDK;}@PreDestroypublic void destroy() {if (null != hCNetSDK) {log.info("释放hcNetSDK资源");hCNetSDK.NET_DVR_Cleanup();}}
}

这个类我也不太清楚干啥的,不过源码中有就拿出来了

@Slf4j
public class FExceptionCallBackException implements HCNetSDK.FExceptionCallBack {@Overridepublic void invoke(int dwType, int lUserID, int lHandle, Pointer pUser) {log.error("异常事件类型:"+dwType);}
}

用来登录的工具类


/*** 监控工具类*/
@Slf4j
public class MonitorUtil {private static int userId = -1;/*** 登录设备,支持 V40 和 V30 版本,功能一致。** @return 登录成功返回用户ID,失败返回-1*/public static int loginDevice() {if (userId != -1) {return userId;}MonitorProperties bean = SpringUtils.getBean(MonitorProperties.class);HCNetSDK hCNetSDK = SpringUtils.getBean(HCNetSDK.class);String ip = bean.getIp();short port = bean.getPort();String user = bean.getUser();String psw = bean.getPassword();// 创建设备登录信息和设备信息对象HCNetSDK.NET_DVR_USER_LOGIN_INFO loginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();HCNetSDK.NET_DVR_DEVICEINFO_V40 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();// 设置设备IP地址byte[] deviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];byte[] ipBytes = ip.getBytes();System.arraycopy(ipBytes, 0, deviceAddress, 0, Math.min(ipBytes.length, deviceAddress.length));loginInfo.sDeviceAddress = deviceAddress;// 设置用户名和密码byte[] userName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];byte[] password = psw.getBytes();System.arraycopy(user.getBytes(), 0, userName, 0, Math.min(user.length(), userName.length));System.arraycopy(password, 0, loginInfo.sPassword, 0, Math.min(password.length, loginInfo.sPassword.length));loginInfo.sUserName = userName;// 设置端口和登录模式loginInfo.wPort = port;loginInfo.bUseAsynLogin = false; // 同步登录loginInfo.byLoginMode = 0; // 使用SDK私有协议if (userId == -1) {synchronized (MonitorUtil.class) {// 执行登录操作userId = hCNetSDK.NET_DVR_Login_V40(loginInfo, deviceInfo);if (userId == -1) {log.error("登录失败,错误码为: {}", hCNetSDK.NET_DVR_GetLastError());} else {log.info("{}设备登录成功! ", ip);// 处理通道号逻辑int startDChan = deviceInfo.struDeviceV30.byStartDChan;log.info("预览起始通道号: {}", startDChan);}}}return userId; // 返回登录结果}}

PlayCtrl 让我单独抽取出来了

//播放库函数声明,PlayCtrl.dll
public interface PlayCtrl extends Library {public static final int STREAME_REALTIME = 0;public static final int STREAME_FILE = 1;boolean PlayM4_GetPort(IntByReference nPort);boolean PlayM4_OpenStream(int nPort, Pointer pFileHeadBuf, int nSize, int nBufPoolSize);boolean PlayM4_InputData(int nPort, Pointer pBuf, int nSize);boolean PlayM4_CloseStream(int nPort);boolean PlayM4_SetStreamOpenMode(int nPort, int nMode);boolean PlayM4_Play(int nPort, W32API.HWND hWnd);boolean PlayM4_Stop(int nPort);boolean PlayM4_SetSecretKey(int nPort, int lKeyType, String pSecretKey, int lKeyLen);boolean PlayM4_GetPictureSize(int nPort, IntByReference pWidth, IntByReference pHeight);boolean PlayM4_GetJPEG(int nPort, Pointer pBitmap, int nBufSize, IntByReference pBmpSize);int PlayM4_GetLastError(int nPort);boolean PlayM4_SetDecCallBackExMend(int nPort, DecCallBack decCBFun, Pointer pDest, int nDestSize, int nUser);public static interface DecCallBack extends Callback {void invoke(int nPort, Pointer pBuf, int nSize, FRAME_INFO pFrameInfo, int nReserved1, int nReserved2);}public class FRAME_INFO extends Structure {public int nWidth;                   /* 画面宽,单位像素。如果是音频数据,则为音频声道数 */public int nHeight;                     /* 画面高,单位像素。如果是音频数据,则为样位率 */public int nStamp;                           /* 时标信息,单位毫秒 */public int nType;                            /* 数据类型,T_AUDIO16, T_RGB32, T_YV12 */public int nFrameRate;                /* 编码时产生的图像帧率,如果是音频数据则为采样率 */public int dwFrameNum;                      /* 帧号 *///高版本方法覆盖@Overrideprotected List getFieldOrder() {return Arrays.asList("nWidth","nHeight","nStamp","nType","nFrameRate","dwFrameNum");}}}

2.25、对应的controller

HcVideoUtil 就是对应的源码中的VideoDemo,这个文件太大,就不粘贴了

@RestController
@RequestMapping("monitorvideo")
public class MonitorVideoController {@Autowiredprivate HcVideoUtil hcVideoUtil;@PostMapping("getvideo")public void getVideo(String channel, Date startTime, Date endTime, String videoFile) {int userId = MonitorUtil.loginDevice();hcVideoUtil.dowmloadRecordByTime(userId, Integer.parseInt(channel), startTime, endTime, videoFile);}
}

大体上就这些文件,额外需要自己在改改就能跑了,接下来就要说踩坑了,这才是重点(linux部署也放在踩坑里,这个是踩坑最多的)

三、踩坑

3.1、 Structure.getFieldOrder() 错误解决办法

可参考这篇文章,https://www.cnblogs.com/easyidea/p/16490708.html
使用他的工具类需要注意,就像博主说的,有些情况并没有处理,所以有的生成的getFieldOrder里面字段是多的,具体什么情况多的有些忘了,距离完成写这篇文章差不多过了半个月。。。
当然,要是没有用其他的jna版本,这个问题是不会出现的

3.2、linux环境部署

首先将对应的文件上传到响应的文件目录下,对应yaml中的文件路径
在这里插入图片描述
启动应该会报找不到一些配置文件,此时我们就参考官方文档
在这里插入图片描述
于是,我们就去找这个东西如何添加到变量中
参考这个
https://blog.csdn.net/x_y_csdn/article/details/110521330
完事后执行下这个命令 ldconfig
此时启动应该就不报错了

3.3、docker环境部署

首先说下,以jdk作为基础镜像是很难搞的,因为你不知道到底还缺少了多少动态库,所以建议直接以linux作为基础镜像,贴下我的dockerfile文件
LD_LIBRARY_PATH=/home/ypx/monitorvideo/lib:$LD_LIBRARY_PATH \ 就是上面提到的环境变量

FROM docker.m.daocloud.io/library/ubuntu:20.04# 设置工作目录和临时卷
WORKDIR /app
VOLUME /tmp# 更新apk包索引并安装ffmpeg
RUN apt-get update && \apt-get install -y ffmpeg && \apt-get install -y openjdk-8-jdk 
# 复制本地编译好的 jar 文件(从target目录)
COPY device-supervision.jar app.jar# 设置时区为中国时区
ENV TZ=Asia/Shanghai \LD_LIBRARY_PATH=/home/ypx/monitorvideo/lib:$LD_LIBRARY_PATH \#设置中文不乱码的	LANG=C.UTF-8 \LC_ALL=C.UTF-8
# 暴露端口
EXPOSE 18881# 设置 Java 运行参数
ENTRYPOINT ["java", \"-Djava.security.egd=file:/dev/./urandom", \"-XX:+UseContainerSupport", \"-XX:MaxRAMPercentage=75.0", \"-jar", \"/app/app.jar"]

启动命令
docker run -d -p 18881:18881 --privileged=true -v /home/ypx/monitorvideo/:/home/ypx/monitorvideo/ --restart always --name device device:0.0.1

3.4、下载下来的视频可以本地播放,网页上放不了

https://blog.csdn.net/xcg340123/article/details/139825982
简单来说就是网页不支持这种海康格式的视频文件,我是用ffmpeg 转换的,贴下工具类

@Slf4j
public class VideoUtil {/*** 海康下载来的视频无法直接在浏览器播放,需要进行转码** @param oldPath 旧的地址* @param newPath 新的地址*/public static void convert(String oldPath, String newPath, String ffmpegPath) {try {//需要区分是否为linux环境if (OsSelectUtil.isLinux()) {ffmpegPath = "ffmpeg";}List<String> command = new ArrayList<String>();command.add(ffmpegPath);command.add("-i");command.add(oldPath);command.add("-c");command.add("copy");command.add("-an");command.add(newPath);ProcessBuilder builder = new ProcessBuilder(command);Process process = builder.start();//等待转换完成process.waitFor();log.info("转换视频{}完成!", newPath);//需要删除源文件FileUtil.del(oldPath);} catch (Exception e) {log.error("转换视频出错!");log.error(e.getMessage(), e);}}}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com