1.开发准备
Maven依赖
<properties>
<java.version>1.8</java.version>
<mybatisplus.version>3.2.0</mybatisplus.version>
<druid.version>1.1.20</druid.version>
<shiro.version>1.4.0</shiro.version>
<log4j.version>1.2.17</log4j.version>
<hutool.version>4.6.8</hutool.version>
<pinyin4j.version>2.5.1</pinyin4j.version>
<fastjson.version>1.2.62</fastjson.version>
<javacv.version>1.5.3</javacv.version>
<javacv.windows-x86_64>windows-x86_64</javacv.windows-x86_64>
</properties>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>${javacv.version}</version>
<exclusions>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>flycapture</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libdc1394</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>videoinput</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>artoolkitplus</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>flandmark</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>leptonica</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>tesseract</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.2.2-${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.9-${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.3.0-${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
2.定时任务
从数据库中查询视频监控参数
设置文件路径和截图文件名称
开始通过ffmpeg推流,截取其中一帧保存
/***
* 定时获取video数据
*/
@Scheduled(fixedRate = 10800000)//一小时执行一次
@Async
public void getVideoPic() {
log.info("定时任务开始:获取VIDEO数据");
Date now=new Date();
try {
//获取可用VIDEO
List<VideoExt> videoList = videoService.getStationNameByDeptId(19, null);
if (videoList.size() > 0) {
//遍历VIDEO
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dayStr = sdf.format(now);
int hour = now.getHours();
int minute = now.getMinutes();
for (VideoExt video : videoList) {
String ip = video.getCameraIp();
if (StringUtils.isNotBlank(ip)){
//存储图片目录路径
String path = "D:\\download\\" + video.getLineName() + "\\" + video.getStationName() + "\\" + dayStr + "\\";
File file=new File(path);
if (!file.exists()) {
file.mkdirs();
}
//视频配置
VideoConfig config = JSONObject.parseObject(video.getConfig(), VideoConfig.class);
//绝对路径
String targetName =path+ip + "-1-" + hour+minute + ".jpg";
//推流rtsp命令
String url=VideoUtil.replaceRtspUrl(video.getCameraType(),video.getCameraMode(),video.getCameraUserName(),video.getCameraPassword(),
video.getCameraIp(),String.valueOf(config.getPic1()+1));
//开始推流和截图
VideoUtil.getVideoImagePathByRSTP(url,targetName);
}
}
}
} catch (Exception e) {
log.error(":获取VIDEO数据错误"+e.getMessage());
}
log.info("定时任务结束:获取video数据");
}
推流url
private static String HK_NVR_RTSP_URL="rtsp://{username}:{password}@{ip}:554/Streaming/Channels/{channel}01";
private static String HK_CAMERA_RTSP_URL="rtsp://{username}:{password}@{ip}:554/Streaming/Channels/{channel}01";
private static String DH_RTSP_URL="rtsp://{username}:{password}@{ip}:554/cam/realmonitor?channel={channel}&subtype=0";
推流url替换参数
public static String replaceRtspUrl(String cj,String mode,String userName,String password,String ip,String channel){
if(cj.equals("海康")){
if(mode.equals("NVR")){
return HK_NVR_RTSP_URL.replace("{username}",userName)
.replace("{password}",password)
.replace("{ip}",ip).replace("{channel}",channel);
}else {
return HK_CAMERA_RTSP_URL.replace("{username}",userName)
.replace("{password}",password)
.replace("{ip}",ip).replace("{channel}",channel);
}
}else if(cj.equals("大华")){
return DH_RTSP_URL.replace("{username}",userName)
.replace("{password}",password)
.replace("{ip}",ip).replace("{channel}",channel);
}
return "";
}
核心方法
FFmpegFrameGrabber rstp流对象
原理
/**
* 解析视频地址并截图
* @param path rstp 流地址
* @param picPath 图片存放地址
* @throws Exception
*/
public static String getVideoImagePathByRSTP(String path, String picPath) throws Exception {
log.info("开始拉流"+path+picPath);
System.setProperty("org.bytedeco.javacpp.maxphysicalbytes", "0");
System.setProperty("org.bytedeco.javacpp.maxbytes", "0");
String ret=picPath;
//创建rstp流对象
FFmpegFrameGrabber grabber = createGrabber(path);
try {
//开启流获取
grabber.startUnsafe();
//由于视频第一帧的流可能为黑屏 为了确保实时能截取到准确图像
// 故此做了个for循环用于覆盖生成图片
for (int i=0;i<count;i++){
//获取流视频框内的图像
Frame frame = grabber.grabFrame();
//转换图像
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage srcImage = converter.getBufferedImage(frame);
//自定义画图
if (srcImage!=null) {
//创建文件
File file = new File(picPath);
//输出文件
ImageIO.write(srcImage, "jpg", file);
}
}
log.info("结束拉流");
return ret.replaceAll("\\\\","//");
}catch (Exception e){
ret="异常";
log.error(e.getMessage());
}finally {
if(grabber!=null) {
grabber.stop();
grabber.close();
grabber.release();
}
}
return ret;
}