Java调用ffmpeg进行视频转换

最近客户不知道从哪里拿来了很多MP4,上传到系统后无法在网页上直接用H5播放。
用格式工厂将编码改为H264也播放不了,无奈之下,查询了一下网上的资料,用ffmpeg自己写了上传文件的转码功能。
首先,去 https://ffmpeg.zeranoe.com/builds/ 下载最新的ffmpeg的static版,解压后找到bin下面的ffmpeg.exe,拷到你的项目下。
上传并调用ffmpeg:可直接调用uploadSingleVideoFil()方法

FileUploadUtil.java:

package org.sun.com;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.multipart.MultipartFile;

public class FileUploadUtil {
	
	private static final Logger logger = LoggerFactory
			.getLogger(FileUploadUtil.class);
	
	public static final String ROOT_PATH = ContextLoader.getCurrentWebApplicationContext().getServletContext().getRealPath("").replace("\\",File.separator);

	public static final String UPLOAD_ROOT_PATH = "upload";
	
        public static final String UPLOAD_VIDEO_PATH="videos";
		
	public static String getUploadPath(String path){
		return ROOT_PATH+File.separator+UPLOAD_ROOT_PATH+path;
	}
		
	public static String getUploadRootPath(){
		return getUploadPath("");
	};
	
	public static String getUploadIMGPath(){
		return getUploadPath(File.separator+UPLOAD_IMG_PATH);
	}
	
	public static File mkDir(String path){
		File dir = new File(path);
		if(!dir.exists())
			dir.mkdirs();
		return dir;
	}
	
	//生成下载根目录
	public static File mkUploadRootDir(){
		return mkDir(getUploadRootPath());
	}
	
	/**
	 * 
	 * @param originalFileName
	 * @return 返回原文件的后缀 
	 * 
	 */
	public static String getOriginalFileSuffix(String originalFileName){
		int index=originalFileName.lastIndexOf(".");
		if(index!=-1){
			return originalFileName.substring(index);
		}else
			return originalFileName;
	}
	
	/**
	 * 
	 * @param path 这个path 是upload的子目录路径
         * @param orginalFileName
	 * @return
	 */
	public static File createServerFile(String path,String orginalFileName){
		// Creating the directory to store file
		File dir = mkDir(getUploadPath(path));
		
		String orginalFileNameSuffix = getOriginalFileSuffix(orginalFileName);
		
		// Create the file on server
		File serverFile = new File(dir.getAbsolutePath()
				+ File.separator +new Date().getTime()+orginalFileNameSuffix);
		
		return serverFile;
	}
	
	/**
	 * 
	 * @param file
	 * @return 返回从upload目录下面的相对路径 */
	public static String getRelativePathFromUploadDir(File file){
		if(null==file)
			return "";
		String absolutePath = file.getAbsolutePath();
		if(absolutePath.indexOf(ROOT_PATH)!=-1 && ROOT_PATH.length()-1) && (dot < (serverFile.getAbsolutePath().length()))) { 
					String codcFilePath = serverFile.getAbsolutePath().substring(0, dot) + "_changed.mp4";
					
					ExecuteCodecs executeCodecs = new ExecuteCodecs();
					executeCodecs.exchangeToMp4(ffmpegPath, serverFile.getAbsolutePath(), codcFilePath);
					
					File newFile = new File(codcFilePath);
                                        return getRelativePathFromUploadDir(newFile).replaceAll("\\\\", "/");
	                        }
				else
					return null;	
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println(e.getMessage());
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println(e.getMessage());
			}
		
		}else{
			System.out.println("文件内容为空");
		}
		return null;	
	}
	
}

ExecuteCodecs.java:

package org.sun.com;

import java.util.ArrayList;
import java.util.List;
public class ExecuteCodecs {
	
	/** 
     * 视频转码 (PC端MP4)
     * @param ffmpegPath    转码工具的存放路径
     * @param upFilePath    用于指定要转换格式的文件,要截图的视频源文件
     * @param codcFilePath    格式转换后的的文件保存路径
     * @return 
     * @throws Exception 
     */  
    public boolean exchangeToMp4(String ffmpegPath, String upFilePath, String codcFilePath) throws Exception {  
        // 创建List集合来保存转换视频文件为flv格式的命令 
        List convert = new ArrayList();  
        convert.add(ffmpegPath); // 添加转换工具路径  
        convert.add("-y"); // 该参数指定将覆盖已存在的文件  
        convert.add("-i");
        convert.add(upFilePath);
        convert.add("-c:v");
        convert.add("libx264");
        convert.add("-c:a");
        convert.add("aac");
        convert.add("-strict");
        convert.add("-2");
        convert.add("-pix_fmt");
        convert.add("yuv420p");
        convert.add("-movflags");
        convert.add("faststart");
        //convert.add("-vf");   // 添加水印
        //convert.add("movie=watermark.gif[wm];[in][wm]overlay=20:20[out]");
        convert.add(codcFilePath);  
  
        boolean mark = true;  
                
        try {  
            Process videoProcess = new ProcessBuilder(convert).redirectErrorStream(true).start();            
            new PrintStream(videoProcess.getInputStream()).start();                        
            //videoProcess.waitFor();  // 加上这句,系统会等待转换完成。不加,就会在服务器后台自行转换。
            
        } catch (Exception e) {  
            mark = false;  
            System.out.println(e);  
            e.printStackTrace();  
        }  
        return mark;  
    } 
}

PrintStream.java:
package org.sun.com;

public class PrintStream extends Thread 
{
     java.io.InputStream __is = null;
     public PrintStream(java.io.InputStream is) 
     {
         __is = is;
     } 
 
     public void run() 
     {
         try 
         {
             while(this != null) 
             {
                 int _ch = __is.read();
                 if(_ch != -1) 
                     System.out.print((char)_ch); 
                 else break;
             }
         } 
         catch (Exception e) 
         {
             e.printStackTrace();
         } 
     }
 }

其他:
在客户的服务器上部署的时候遇到这样一个问题。
我上传个500M的文件它可以正常转换;上传个1G的文件,ffmpeg转到1分钟左右就停住了。
直接在cmd中打ffmpeg命令也是同样。
可是同样的文件同样的命令,在我们开发的机器上就没问题,可以转换完成。
开发机的系统是win7,客户服务器是windows server 2008 R2,可能是由于系统原因导致ffmpeg内存溢出?
无奈之下下载了最新的ffmpeg-20170327-d65b595-win64-static试试,竟然ok了。
可能是我以前用的是老版本的ffmpeg,里面有一些问题,在新版本中已经被修正了吧。
接下来有空我要好好学学ffmpeg视频转码的原理了。

你可能感兴趣的:(Java)