终于初步把java上传视频后,调用ffmpeg处理视频完成的差不多了,困扰我的主要有两个问题,第一个是:怎么控制转码之后再进行截取视频,第二个是:进程以及线程的处理问题。目前都已解决,以下均是大白话,资深人士莫要笑话。ffmpeg.exe链接:http://download.csdn.net/detail/jiaxiaoteng/8175987,源代码链接:http://download.csdn.net/detail/jiaxiaoteng/8175787,源代码要了一分,实在是穷呐,大家莫怪啊,下载后直接用MyEclipse导入运行即可。好了,废话不多说了,直接贴代码开搞:
java上传视频:(这个没啥说的,就是最简单的上传,上传完之后调用视频处理类)
package dealwithVideo;
import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
*
* @author jxt
*
* @date 2011-2014-10-10
*
*/
public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = -7744625344830285257L;
// private ServletContext sc;
// private String savePath;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void init(ServletConfig config) {
// 在web.xml中设置的一个初始化参数
// savePath = config.getInitParameter("savePath");
// sc = config.getServletContext();
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
@SuppressWarnings("rawtypes")
List items;
FileItem item =null;
try {
items = upload.parseRequest(request);
@SuppressWarnings("rawtypes")
Iterator itr = items.iterator();
while (itr.hasNext()) {
item = (FileItem) itr.next();
if (item.isFormField()) {
// System.out.println("表单参数名:" + item.getFieldName() + ",表单参数值:" + item.getString("UTF-8"));
} else {
if (item.getName() != null && !item.getName().equals("")) {
// System.out.println("上传文件的大小:" + item.getSize());
// System.out.println("上传文件的类型:" + item.getContentType());
// // item.getName()返回上传文件在客户端的完整路径名称
// System.out.println("上传文件的名称:" + item.getName());
File tempFile = new File(item.getName());
//上传文件的保存路径
// String wholePath=sc.getRealPath("\\") + savePath+"\\";
String wholePath = "e:\\ffmpeg\\";
String wholeFilename = tempFile.getName();
File file = new File(wholePath, wholeFilename);
System.out.println("开始上传");
item.write(file);
System.out.println("上传完成\t文件上传到---"+wholePath+wholeFilename);
ConvertVideo cv = new ConvertVideo();
cv.dealVideo(wholePath, tempFile.getName());
request.setAttribute("upload.message", "OK");
}else{
request.setAttribute("upload.message", "没有选择上传文件!");
}
}
}
}catch(FileUploadException e){
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("upload.message", "Excel文件错误,上传文件失败!");
}
request.getRequestDispatcher("/uploadResult.jsp").forward(request, response);
}
}
ConvertVideo视频处理类:(这个类中我想说的有两点,第一个是new PrintStream()的作用:在调用ffmpeg转码时,会不停的往inputstream和errorstream中写数据,如果不及时读取,jvm内存满了,就会导致写不进去,进程全面堵车,死掉------(进程堵塞)。第二个是waitfor(),这个方法作用是当前进程完成(返回)后,在进行其他进程------(进程等待))
package dealwithVideo;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
public class ConvertVideo {
// private final static String PATH = "F:\\ffmpeg\\1.mp4";
//处理视频
public void dealVideo(String wholePath,String filename) {
String videoPath = wholePath+filename;//视频路径+视频名称
if (!checkfile(videoPath)) { //检查视频是否存在
System.out.println(videoPath + " is not file");
return;
}
if (process(wholePath,filename)) {
System.out.println("转码并截图完成");
}
//截取视频
String filenameBef = filename.substring(0, filename.length()-4);
System.out.println("开始截取视频");
String cutVideosStatus = new CutVideo().cutVideos(wholePath,filenameBef);
System.out.println(cutVideosStatus);
}
//检查视频是否存在
private static boolean checkfile(String path) {
File file = new File(path);
if (!file.isFile()) {
return false;
}
return true;
}
//转码并截图
private static boolean process(String wholePath,String filename) {
int type = checkContentType(filename); //查看视频类型
if(type==0){//如果不是flv,则转码为flv并获取缩略图
System.out.println("视频格式不是flv,进行转码....");
return processFLV(wholePath,filename);// 将其他格式转为flv
}
else{
System.out.println("视频格式是flv,开始截图....");
return getPic(wholePath,filename);//如果是flv格式则直接获取缩略图
}
}
//查看视频类型 type=0为其他类型,9为flv
private static int checkContentType(String filename) {
String type = filename.substring(filename.length()-3,filename.length());
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;
}
return 9;
}
private static boolean getPic(String wholePath,String filename){
String filenameBef = filename.substring(0, filename.length()-4);
//截取缩略图
List<String> commend1 = new ArrayList<String>();
commend1.add("F:\\ffmpeg\\ffmpeg.exe");
commend1.add("-i");
commend1.add(wholePath+filename);
commend1.add("-y");
// commend1.add("-f");
commend1.add("image2");
commend1.add("-ss");
commend1.add("8");
commend1.add("-s");
commend1.add("600x500");
commend1.add("f:\\ffmpeg\\"+filenameBef+".jpg");
// System.out.println(commend1);
try {
Runtime runtime = Runtime.getRuntime();
Process proce = null;
String ffmpegStr = "E:\\ffmpeg\\ffmpeg.exe -i "+wholePath+filename+" -f image2 " +
"-ss 5 -s 600*500 " +
"e:\\ffmpeg\\"+filenameBef+".jpg";
proce = runtime.exec(ffmpegStr);
//获取视频总时长
Scanner sc = new Scanner(proce.getErrorStream());
// Find duration
Pattern durPattern = Pattern.compile("(?<=Duration: )[^,]*");
String dur = sc.findWithinHorizon(durPattern, 0);
if (dur == null)
throw new RuntimeException("Could not parse duration.");
String[] hms = dur.split(":");
double totalSecs = Integer.parseInt(hms[0]) * 3600
+ Integer.parseInt(hms[1]) * 60
+ Double.parseDouble(hms[2]);
System.out.println("视频总时长: " + totalSecs + "秒");
new PrintStream(proce.getErrorStream()).start();
new PrintStream(proce.getInputStream()).start();
proce.waitFor();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
// ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
private static boolean processFLV(String wholePath,String filename) {
if (!checkfile(wholePath+filename)) {
System.out.println(wholePath+filename+ " is not file");
return false;
}
String filenameBef = filename.substring(0, filename.length()-4);
// 文件命名
// Calendar c = Calendar.getInstance();
// String savename = String.valueOf(c.getTimeInMillis())+ Math.round(Math.random() * 100000);
//转化视频
List<String> commend = new ArrayList<String>();
commend.add("E:\\ffmpeg\\ffmpeg.exe");
commend.add("-i");
commend.add(wholePath+filename);
commend.add("-b");
commend.add("512");
commend.add("-ab");
commend.add("128");
commend.add("-ar");
commend.add("44100");
commend.add("-qscale");
commend.add("8");
commend.add("-r");
commend.add("25");
// commend.add("-s");
// commend.add("600x500");
commend.add("-aspect");
commend.add("4:3");
commend.add("e:\\ffmpeg\\"+filenameBef+".flv");
try {
Runtime runtime = Runtime.getRuntime();
Process proce = null;
String cmd = "";
String cut = " e:\\ffmpeg\\ffmpeg.exe -i "
+ wholePath+filename
+ " -f image2 -ss 5 -s 600x480 e:\\ffmpeg\\"
+ filenameBef+".jpg";
String cutCmd = cmd + cut;
proce = runtime.exec(cutCmd);
// ProcessBuilder builder = new ProcessBuilder(commend);
// builder.command(commend);
// builder.start();
Process videoProcess = new ProcessBuilder(commend).start();
new PrintStream(videoProcess.getErrorStream()).start();
new PrintStream(videoProcess.getInputStream()).start();
//获取视频总时长
Scanner sc = new Scanner(proce.getErrorStream());
// Find duration
Pattern durPattern = Pattern.compile("(?<=Duration: )[^,]*");
String dur = sc.findWithinHorizon(durPattern, 0);
if (dur == null)
throw new RuntimeException("Could not parse duration.");
String[] hms = dur.split(":");
double totalSecs = Integer.parseInt(hms[0]) * 3600
+ Integer.parseInt(hms[1]) * 60
+ Double.parseDouble(hms[2]);
System.out.println("视频总时长: " + totalSecs + "秒");
videoProcess.waitFor();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
视频截取类
package dealwithVideo;
public class CutVideo {
public String cutVideos(String wholePath,String filename){
// String filenameBef = filename.substring(0, filename.length()-4);
try {
Runtime runtime = Runtime.getRuntime();
Process proce = null;
String ffmpegStr = "E:\\ffmpeg\\ffmpeg.exe -ss 00:00:00 -i "+wholePath+filename+".flv " +
" -acodec copy -vcodec copy -t 00:00:10 " +
wholePath+filename+"-jiequ.flv";
proce = runtime.exec(ffmpegStr);
new PrintStream(proce.getErrorStream()).start();
new PrintStream(proce.getInputStream()).start();
proce.waitFor();
return "视频截取成功,截取时长为10秒";
} catch (Exception e) {
e.printStackTrace();
return "视频截取失败";
}
}
}
PrintStream类
package dealwithVideo;
import java.io.InputStream;
public class PrintStream extends Thread{
InputStream ism = null;
public PrintStream(InputStream is){
ism = is;
}
public void run(){
try{
while(this != null){
int ch = ism.read();
if(ch!=-1){
System.out.print((char)ch);
}else{
break;
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
还有一个小问题,就是在上传的时候,点了提交按钮,直到视频处理完后才会跳转,这个好解决,在jsp页面引用jQuery-from.js,java上传类中上传完成后先不要调用视频处理类,在前台判断上传完成后再由前台ajax调用视频处理类,即可达到上传完成立马页面跳转,后台开始处理视频。