本文是记录自己学习过程的,不适合直接拿来用的
业务需求及场景
后台基于spring boot 的微服务框架,页面是vue
用户可以选择一个视频上传的管理页面,新增时,用户可以选择上传封面图片或者不选择上传封面图片(此时要根据视频的格式决定是从视频中截图或者展示默认图片)
页面效果先不展示了,涉及的信息比较多
这里贴的是视频转码功能部分,我从网上找的代码又根据自己的需要又改的,很佩服写这段代码的人
真的很厉害(侵删)
/**
* @author wuad
*/
public class Contants {
/**
* 本地测试
*/
public static final String ffmpegpath = "D:/ffmpeg-20180723-d134b8d-win64-static/bin/ffmpeg.exe"; // ffmpeg工具安装位置
public static final String targetfolder = "C:/linshiVideo/"; // 转码后视频保存的目录
public static final String imageRealPath = "C:/linshiVideo/"; // 截图的存放目录
}
/**
* @author wuad
*/
@RestController
@RequestMapping(value = "/video/transition/")
public class ConverVideoTest {
//web调用
@GetMapping(value = "/run/{yuanPATH}")
public Result ffmpeg(@PathVariable("yuanPATH") String yuanPATH) {
try {
//web传入的源视频
String filePath = yuanPATH;
System.out.println("ConverVideoTest说:传入工具类的源视频地址为:"+filePath);
//传入path
ConverVideoUtils zout = new ConverVideoUtils(filePath);
//设置转换的格式
String targetExtension = ".mp4";
boolean isDelSourseFile = true;
//开始调用转换方法,删除源文件。
boolean beginConver = zout.beginConver(targetExtension,isDelSourseFile);
System.out.println("beginConver:" + beginConver);
} catch (Exception e) {
e.printStackTrace();
}
return Res.makeOKRsp("视频转码成功");
}
}
/**
* @author wuad
*/
public class ConverVideoUtils {
private String sourceVideoPath; //源视频路径
private String filerealname; //文件名不包括后缀名
private String filename; //包括后缀名
private String ffmpegpath = Contants.ffmpegpath; // ffmpeg.exe的目录
private String imageRealPath = Contants.imageRealPath; // 视频截图的目录
//重构构造方法,传入源视频
public ConverVideoUtils(String path) {
sourceVideoPath = path;
}
//set和get方法传递path
public String getPATH() {
return sourceVideoPath;
}
public void setPATH(String path) {
sourceVideoPath = path;
}
/**
* 转换视频格式
* @param targetExtension 目标视频后缀名 .xxx
* @param isDelSourseFile 转换完成后是否删除源文件
* @return
*/
public boolean beginConver(String targetExtension, boolean isDelSourseFile) {
File fi = new File(sourceVideoPath);
//获取视频名+后缀名
filename = fi.getName();//视频转码后,路径加这个反馈给前台显示出视频和封面图片
//获取不带后缀的文件名-后面加.toLowerCase()小写(filerealname:视频名称)
filerealname = filename.substring(0, filename.lastIndexOf("."));
System.out.println("----接收到文件("+filerealname+")需要转换-------");
//执行转码机制
if (process(targetExtension,isDelSourseFile)) {
System.out.println("视频转码结束,开始截图================= ");
//视频转码完成,调用截图功能
if (processImg(sourceVideoPath)) {
System.out.println("截图成功! ");
} else {
System.out.println("截图失败! ");
}
File file1 = new File(sourceVideoPath);
if (file1.exists()){
System.out.println("删除原文件-可用:"+sourceVideoPath);
file1.delete();
}
sourceVideoPath = null;
return true;
} else {
sourceVideoPath = null;
return false;
}
}
/**
* 视频截图功能
* @param sourceVideoPath 需要被截图的视频路径(包含文件名和后缀名)
* @return
*/
public boolean processImg(String sourceVideoPath) {
//先确保保存截图的文件夹存在
File TempFile = new File(imageRealPath);
if (TempFile.exists()) {
if (TempFile.isDirectory()) {
System.out.println("该文件夹存在。");
}else {
System.out.println("同名的文件存在,不能创建文件夹。");
}
}else {
System.out.println("文件夹不存在,创建该文件夹。");
TempFile.mkdir();
}
File fi = new File(sourceVideoPath);
filename = fi.getName(); //获取视频文件的名称。
filerealname = filename.substring(0, filename.lastIndexOf(".")); //获取视频名+不加后缀名 后面加.toLowerCase()转为小写
List<String> commend = new ArrayList<String>();
//第一帧: 00:00:01
//截图命令:time ffmpeg -ss 00:00:01 -i test1.flv -f image2 -y test1.jpg
commend.add(ffmpegpath); //指定ffmpeg工具的路径
commend.add("-ss");
commend.add("00:00:10"); //1是代表第1秒的时候截图
commend.add("-i");
commend.add(sourceVideoPath); //截图的视频路径
commend.add("-f");
commend.add("image2");
commend.add("-y");
commend.add(imageRealPath + filerealname +".jpg"); //生成截图xxx.jpg
//打印截图命令
StringBuffer test = new StringBuffer();
for (int i = 0; i < commend.size(); i++) {
test.append(commend.get(i) + " ");
}
System.out.println("截图命令:"+test);
System.out.println("截图名称:"+ filerealname +".jpg");
//转码后完成截图功能-还是得用线程来解决
try {
/*ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
Process p =builder.start();*/
//调用线程处理命令
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
Process p = builder.start();
//获取进程的标准输入流
final InputStream is1 = p.getInputStream();
//获取进程的错误流
final InputStream is2 = p.getErrorStream();
//启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
new Thread() {
public void run() {
BufferedReader br = new BufferedReader(
new InputStreamReader(is1));
try {
String lineB = null;
while ((lineB = br.readLine()) != null) {
if (lineB != null){
//System.out.println(lineB); //必须取走线程信息避免堵塞
}
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭流
finally{
try {
is1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
BufferedReader br2 = new BufferedReader(
new InputStreamReader(is2));
try {
String lineC = null;
while ((lineC = br2.readLine()) != null) {
if (lineC != null) {
//System.out.println(lineC); //必须取走线程信息避免堵塞
}
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭流
finally{
try {
is2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
// 等Mencoder进程转换结束,再调用ffmepg进程非常重要!!!
p.waitFor();
System.out.println("截图进程结束");
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 实际转换视频格式的方法
* @param targetExtension 目标视频后缀名
* @param isDelSourseFile 转换完成后是否删除源文件
* @return
*/
private boolean process(String targetExtension, boolean isDelSourseFile) {
//先判断视频的类型-返回状态码
int type = checkContentType();
boolean status = false;
// 根据状态码处理
if (type == 0) {
System.out.println("ffmpeg可以转换,统一转为mp4文件");
// 可以指定转换为什么格式的视频
status = processVideoFormat(sourceVideoPath,targetExtension,isDelSourseFile);
}
return status; //执行完成返回布尔类型true
}
/**
* 检查文件类型,判断ffmpeg能否转换
* @return
*/
private int checkContentType() {
//取得视频后缀-
String type = sourceVideoPath.substring(sourceVideoPath.lastIndexOf(".") + 1, sourceVideoPath.length()).toLowerCase();
System.out.println("源视频类型为:"+type);
// 如果是ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
if (type.equals("avi")) {
return 0;
} else if (type.equals("mpg")) {
return 0;
} else if (type.equals("wmv")) {
return 0;
} else if (type.equals("3gp")) {
return 0;
} else if (type.equals("mov")) {
return 0;
} else if (type.equals("mp4")) {
return 0;
} else if (type.equals("asf")) {
return 0;
} else if (type.equals("asx")) {
return 0;
} else if (type.equals("flv")) {
return 0;
}else if (type.equals("mkv")) {
return 0;
}
System.out.println("上传视频格式异常");
return 9;
}
/**
* 转换为指定格式
* ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
* @param oldfilepath
* @param targetExtension 目标格式后缀名 .xxx
* @param isDelSourceFile 转换完成后是否删除源文件
* @return
*/
private boolean processVideoFormat(String oldfilepath, String targetExtension, boolean isDelSourceFile) {
System.out.println("这里调用了ffmpeg.exe工具");
System.out.println("----开始转文件(" + sourceVideoPath + ")-------------------------- ");
List<String> commend = new ArrayList<String>();
commend.add(ffmpegpath); //ffmpeg.exe工具地址
commend.add("-i");
commend.add(oldfilepath); //源视频路径
commend.add("-vcodec");
commend.add("h263"); //
commend.add("-ab"); //新增4条
commend.add("128"); //高品质:128 低品质:64
commend.add("-acodec");
commend.add("mp3"); //音频编码器:原libmp3lame
commend.add("-ac");
commend.add("2"); //原1
commend.add("-ar");
commend.add("22050"); //音频采样率22.05kHz
commend.add("-r");
commend.add("29.97"); //高品质:29.97 低品质:15
commend.add("-c:v");
commend.add("libx264"); //视频编码器:视频是h.264编码格式
commend.add("-strict");
commend.add("-2");
commend.add(targetfolder + filerealname + targetExtension); // //转码后的路径+名称,是指定后缀的视频
//打印命令
StringBuffer test = new StringBuffer();
for (int i = 0; i < commend.size(); i++) {
test.append(commend.get(i) + " ");
}
System.out.println("ffmpeg输入的命令:"+test);
try {
//多线程处理加快速度-解决rmvb数据丢失builder名称要相同
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
Process p = builder.start(); //多线程处理加快速度-解决数据丢失
final InputStream is1 = p.getInputStream();
final InputStream is2 = p.getErrorStream();
new Thread() {
public void run() {
BufferedReader br = new BufferedReader(
new InputStreamReader(is1));
try {
String lineB = null;
while ((lineB = br.readLine()) != null) {
if (lineB != null)
System.out.println(lineB); //打印mencoder转换过程代码
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
public void run() {
BufferedReader br2 = new BufferedReader(
new InputStreamReader(is2));
try {
String lineC = null;
while ((lineC = br2.readLine()) != null) {
if (lineC != null)
System.out.println(lineC); //打印mencoder转换过程代码
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
p.waitFor(); //进程等待机制,必须要有,否则不生成mp4!!!
System.out.println("生成mp4视频为:"+imageRealPath + filerealname + ".mp4");
return true;
}
catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
这是controller 的
/**
* 添加视频信息
* @param file
* @return
*/
@PostMapping(value = "/save")
public Result save(Video video, @RequestParam("file") MultipartFile[]file){
// logger.error("[file]" + file.length);
try{
if(video != null){
// 根据视频名称查询,重复的视频名称数据不能添加
List<Video> list = videoService.fingListByVideoName(video.getVideoName());
if(list != null && list.size() > 0) {
return Res.makeErrRsp("视频名称,请勿命名重复");
}else{
if(file != null){
//上传视频
JSONObject rData = videoService.fileUpload(file);
// 获取上传视频后的路径
String data = rData.get("data").toString();
JSONObject rr = JSONObject.parseObject(data);
// 获取基本信息,厂商id,视屏名称,视频上传后的路径
video.setVideoPath(rr.getString("path"));
// 视频名称
video.setUploadVideoName(rr.getString("fileName"));
// // 用户上传的图片名称
video.setCoverPicture(rr.getString("coverPictureName"));
// 视频中截取的图片
video.setJietu(rr.getString("jieTu"));
video.setId(UUID.randomUUID().toString().replace("-",""));
videoService.insert(video);
}else if (video != null && file == null){
logger.error("[添加文件信息失败]");
return Res.makeErrRsp("添加失败,未上传文件");
}else if(video == null && file != null){
logger.error("[添加文件信息失败]");
return Res.makeErrRsp("添加失败,参数为空");
}
}
}
} catch (Exception e){
logger.error("[添加文件信息失败]",e);
return Res.makeErrRsp("添加文件信息失败");
}
return Res.makeOKRsp(video);
}
这是实现类,这个写的比较乱,自己写的很乱,有涉及到很多变量名称的修改和存储
/**
* 视频上传
* @param file
* @return
*/
@Override
public JSONObject fileUpload(MultipartFile [] file) {
JSONObject rData = new JSONObject();
try {
// 视频上传用拼接路径 :/new/video/
String suffixPath = File.separator + "new" + File.separator + "video"+ File.separator ;
String coverPictureName = "";
String fileName = "";
if (file != null && file.length == 1) {
// 获取视频后缀
String suffix = file[0].getOriginalFilename().substring(file[0].getOriginalFilename().lastIndexOf("."));
if (".jpg".equals(suffix.toLowerCase()) || ".png".equals(suffix.toLowerCase()) || ".jpeg".equals(suffix.toLowerCase())) {
// 获取图片名称和后缀格式
String pictureName = file[0].getOriginalFilename().substring(0,file[0].getOriginalFilename().lastIndexOf(".")).replaceAll(" ", "");
// 图片名称拼接
coverPictureName = pictureName + "-" + System.currentTimeMillis() + suffix;
byte[] bytesPicture = file[0].getBytes();
// 上传路径正式环境
Path pathPicture = Paths.get( uploadFilesPath + suffixPath + File.separator+ coverPictureName);
// 图片写入指定路径
Files.write(pathPicture,bytesPicture);
} else {
// 获取视频名称
String uploadFileName = file[0].getOriginalFilename().substring(0,file[0].getOriginalFilename().lastIndexOf(".")).replaceAll(" ", "");
// // 获取视频后缀
// String houzhui = suffix;
System.out.println("需要上传的视频为:" + uploadFileName + suffix);
// 判断视频格式是否是MP4,否,则调用转码方法,转码方法也能将视频上传
// 视频名称拼接
fileName = uploadFileName + "-" + System.currentTimeMillis() + suffix;
byte[] bytes = file[0].getBytes();
// 上传路径
Path path = Paths.get( uploadFilesPath + suffixPath + fileName);
//视频写入指定路径
Files.write(path, bytes);
//
if(!suffix.equals(".mp4")){
coverPictureName = fileName.replace(suffix, ".jpg");
}
}
} else if(file != null && file.length == 2) {
// 获取视频名称和后缀
String uploadFileName = file[0].getOriginalFilename().substring(0,file[0].getOriginalFilename().lastIndexOf(".")).replaceAll(" ", "");
String suffix = file[0].getOriginalFilename().substring(file[0].getOriginalFilename().lastIndexOf("."));
String houzhui = suffix;
System.out.println("视频后缀为:" + suffix);
// 视频名称拼接
fileName = uploadFileName + "-" + System.currentTimeMillis() + suffix;
byte[] bytes = file[0].getBytes();
Path path = Paths.get( uploadFilesPath + suffixPath + fileName);
//视频写入指定路径
Files.write(path, bytes);
// 获取图片名称和后缀格式
String pictureName = file[1].getOriginalFilename().substring(0,file[1].getOriginalFilename().lastIndexOf(".")).replaceAll(" ", "");
String suffixpicture = file[1].getOriginalFilename().substring(file[1].getOriginalFilename().lastIndexOf("."));
// 图片名称拼接
coverPictureName = pictureName + "-" + System.currentTimeMillis() + suffixpicture;
byte[] bytesPicture = file[1].getBytes();
Path pathPicture = Paths.get( uploadFilesPath +suffixPath + File.separator+ coverPictureName);
// 图片写入指定路径
Files.write(pathPicture,bytesPicture);
}
// 将后缀修改后即为视频截图
String jieTu = fileName.replace(fileName.substring(fileName.indexOf(".")+1,fileName.length()),"jpg");
JSONObject rr = new JSONObject();
rr.put("zhuanmaqianfileName",fileName);
rr.put("jieTu",jieTu);
rr.put("coverPictureName",coverPictureName);
rr.put("fileName",fileName);
rData.put("state","0");
rData.put("msg","文件上传成功");
rData.put("data",rr);
} catch (Exception e) {
rData.put("state","1");
rData.put("msg","文件上传失败:" + e);
return rData;
}
return rData;
}