首先,使用场景:
现在视频网站展示列表都是用img标签展示的,动图用的是gif,但是我们上传视频时并没有视屏封面,就这需要上传到服务器时自动成功封面并保存
JavaCV 是一款开源的视觉处理库,基于GPLv2协议,对各种常用计算机视觉库封装后的一组jar包,封装了OpenCV、libdc1394、OpenKinect、videoInput和ARToolKitPlus等计算机视觉编程人员常用库的接口。
此方法的好处是不需要再服务器上安装插件,直接代码中就可以实现视频截取。
我们需要截取视频第一帧,主要用到了ffmpeg和opencv。
我用到的maven的目前最新javacv版本,1.4.2,它应该支持jdk1.7及以上,我项目用的还是jdk1.7.
org.bytedeco
javacv-platform
1.4.2
网上有说用0.8版本的,但用maven打包编译时总是报错,所以索性用最高版本
本来maven直接引用这段会自动下载依赖包,但是全部下载下载我看有500多兆,因为它包括了android,linux,macosx等。一个截取封面功能要给项目增加五百多兆内存这是不能容忍的。
我的服务器是windows 64系统的,所以我只需要windows-x86_64需要的jar包
所以我精简了一下pom依赖
org.bytedeco
javacv
1.4.2
org.bytedeco.javacpp-presets
*
org.bytedeco.javacpp-presets
opencv
3.4.2-1.4.2
org.bytedeco.javacpp-presets
opencv
3.4.2-1.4.2
windows-x86_64
org.bytedeco.javacpp-presets
ffmpeg
4.0.1-1.4.2
org.bytedeco.javacpp-presets
ffmpeg
4.0.1-1.4.2
windows-x86_64
这样我只用了ffmpeg和opencv,占用内存成功缩小到几十兆。
其他平台需要用可以在classifier里面修改成对应的
android-arm
android-arm64
android-x86
android-x86_64
ios-arm
ios-arm64
ios-x86
ios-x86_64
linux-armhf
linux-arm64
linux-ppc64le
linux-x86
linux-x86_64
macosx-x86_64
windows-x86
windows-x86_64
至此引入jar结束。
引入jar包
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
保存视频的方法
@RequestMapping(value = "uploadVideoFiles", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
@ResponseBody
public String uploadVideoFiles(
@RequestParam(value = "file") MultipartFile[] files, //这样接收文件
String classId,
HttpServletRequest request
) {
try {
Map params=new HashMap();
//视频路径
String path="/resource/videos/";
//缩略图
String framefile="/resource/frames/";
//根路径
String basePath=request.getSession().getServletContext().getRealPath("/");
int userId=((TSystemUser)request.getSession().getAttribute("USER")).getUserId();
params.put("classId",classId);
params.put("attachmentType","VIDEO");
params.put("userId",userId);
for (MultipartFile file : files) { //循环保存文件
Map name=uploadFile(path,file, request);
params.put("attachmentUrl",path+name.get("saveName"));
params.put("attachmentName",name.get("fileName"));
String thumbpath=framefile+name.get("saveName").substring(0,name.get("saveName").length()-4)+".jpg";
params.put("attachmentThumbnail",thumbpath);
//调用保存缩略图方法
this.fetchFrame(basePath+path+name.get("saveName"),basePath+thumbpath);
//保存入库
attachmentService.saveFile(params);
// attachmentService.saveImg(path);
}
// 返回前台
return JSON.toJSONString("success");
} catch (Exception e) {
e.printStackTrace();
return JSON.toJSONString("fail");
}
}
public Map uploadFile(String path,MultipartFile file, HttpServletRequest request) throws IOException {
Map result=new HashMap();
//文件原名
String fileName = file.getOriginalFilename();
String basePath=request.getSession().getServletContext().getRealPath("/");
path=basePath+path; //设置文件保存路径
//File tempFile = new File(path, new Date().getTime() + String.valueOf(fileName));
//文件类型
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();
//保存的文件名
String saveName=String.valueOf((new Date()).getTime()).substring(8)+(int)((Math.random()*999+1))+'.'+fileType;
File tempFile = new File(path, String.valueOf(saveName));
if (!tempFile.getParentFile().exists()) { //创建文件夹
tempFile.getParentFile().mkdir();
}
if (!tempFile.exists()) {
tempFile.createNewFile();
}
file.transferTo(tempFile);
result.put("fileName",fileName);
result.put("saveName",saveName);
return result;
}
截取缩略图的方法
//参数:视频路径和缩略图保存路径
public static void fetchFrame(String videofile, String framefile)
throws Exception {
long start = System.currentTimeMillis();
File targetFile = new File(framefile);
FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videofile);
ff.start();
int length = ff.getLengthInFrames();
int i = 0;
Frame f = null;
while (i < length) {
// 去掉前5帧,避免出现全黑的图片,依自己情况而定
f = ff.grabImage();
if ((i > 5) && (f.image != null)) {
break;
}
i++;
}
ImageIO.write(FrameToBufferedImage(f), "jpg", targetFile);
//ff.flush();
ff.stop();
System.out.println(System.currentTimeMillis() - start);
}
public static BufferedImage FrameToBufferedImage(Frame frame) {
//创建BufferedImage对象
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bufferedImage = converter.getBufferedImage(frame);
return bufferedImage;
}
至此,一个视频文件上传后成功生成视频的缩略图
还有很多其他问题,日后再来总结