Java 视频、音频格式转码,借助 ffmpeg 和 mencoder 实现

首先说明一下,这篇文章只是自己有代码洁癖,看了别人的文章后,重新整理了一下,精简压缩了,我都害怕自己这么固执了,(╯﹏╰)

然后吐槽两句,类似的文章从12年就看到有了,也是使用 ffmpeg 和 mencoder,我等小辈又没能力写个转码器,最多也就把文件读进来然后复制一份修改一下后缀Ψ( ̄∀ ̄)Ψ,

最后来正题,需要下载两个东东,ffmpeg 和 mencoder ,问一下度娘就知道她们家在哪里了o( ̄▽ ̄)o ,我的是 Windows,所以就用Windows版本的,来,骑兵走起,上码!

import java.io.File;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.List;
import com.test.config.MyConfig;  
  
public class VideoConvertor implements Runnable {  
	
    private String sourceVideoPath;//源视频路径 ,包括文件名
    private File videoFile;//需要转换的视频文件
    private boolean videoExist;//需要转换的视频文件
    private String fileRealName; // 文件名 不包括扩展名  
    private String targetFolder; // 存放转换后的视频文件夹路径
    private String targetName; // 转换后的名字,不包含后缀
    private String ffmpegPath; // ffmpeg.exe的目录  
    private String mencoderPath; // mencoder的目录  
    private String targetExtension;
    private boolean delSourceFile;
    private boolean screenshot;
    
    /**
     * 能解析的格式:asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等;
     * wmv9,rm,rmvb 将调用 mencoder 先转换成 avi 再调用 ffmpeg 转换,耗时较长
* 注意!!!这里的方法只能单任务执行啊!执行结束后才能进行下一次的转换 * @param sourceVideoPath 源视频路径 ,包括文件名 * @param targetFolder 输出目标文件夹,以\结尾 * @param targetName 输出目标文件名 * @param targetExtension 目标格式,只对视频有效,如果只是做视频截取第一帧不做转换的话就 null 吧; * @param delSourceFile 是否删除源文件,用途是只截取视频第一帧的话是不会删除源文件的 * @param screenshot 转换时是否截取第一帧 */ public VideoConvertor(String sourceVideoPath, String targetFolder, String targetName, String targetExtension, boolean delSourceFile, boolean screenshot) { setSourceVideoPath(sourceVideoPath); this.targetFolder = targetFolder; this.targetName = targetName; this.targetExtension = targetExtension; this.delSourceFile = delSourceFile; this.screenshot = screenshot; // 这里的 ffmpeg 和 mencoder 的路径使用配置文件读取的方式 this.ffmpegPath = MyConfig.getProperty("ffmpegPath"); // ffmpeg.exe的目录 this.mencoderPath = MyConfig.getProperty("mencoderPath"); // mencoder.exe的目录 } /** * 能解析的格式:asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等 * wmv9,rm,rmvb 将调用 mencoder 先转换成 avi 再调用 ffmpeg 转换,耗时较长 * @return */ public boolean convert() { if (!videoExist) { System.out.println(sourceVideoPath + " is not file"); return false; } System.out.println("----开始转文件(" + sourceVideoPath + ")-------------------------- "); long beginTime = System.currentTimeMillis(); if (process(targetExtension)) { long timecha = (System.currentTimeMillis() - beginTime); System.out.println("转换成功 ,耗时:" + sumTime(timecha)); if (delSourceFile && videoFile.delete()) { System.out.println("文件" + sourceVideoPath + "已删除"); } if (screenshot) { screenshot(); } return true; } else { return false; } } /** * 对视频进行截图 * @return */ public boolean screenshot() { if (!videoExist) { System.out.println(sourceVideoPath + " is not file"); return false; } List commend = new java.util.ArrayList(); // 第一帧: 00:00:01,取第一秒那一帧 // time ffmpeg -ss 00:00:01 -i test1.flv -f image2 -y test1.jpg commend.add(ffmpegPath); commend.add("-ss"); commend.add("00:00:01"); commend.add("-i"); commend.add(sourceVideoPath); commend.add("-f"); commend.add("image2"); commend.add("-y"); commend.add(targetFolder + fileRealName + ".jpg"); try { ProcessBuilder builder = new ProcessBuilder(); builder.command(commend); builder.start(); System.out.println("截图成功了! "); return true; } catch (Exception e) { e.printStackTrace(); System.out.println("截图失败了! "); return false; } } /** * 实际转换视频格式的方法 * @param targetExtension 目标视频扩展名 * @return */ private boolean process(String targetExtension) { int type = checkContentType(); boolean status = false; if (type == 1) {// 如果 type 为 1,将其他文件先转换为 avi ,然后在用 ffmpeg 转换为指定格式 System.out.println("非 ffmpeg 支持格式,先转换为 avi,开始转换"); status = processAVI(); if (!status){// avi文件没有得到 ,也就是转换失败了 System.out.println("转换成 avi 格式失败了~~"); return false; } } if (type == 0 || status) {// 如果 type 为 0 用 ffmpeg 直接转换 System.out.println("ffmpeg 开始转换:"); status = processVideoFormat(targetExtension); } return status; } /** * 转换为指定格式 * ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等) * @param targetExtension 目标格式扩展名 .xxx * @return */ private boolean processVideoFormat(String targetExtension) { if (!videoExist) { System.out.println(sourceVideoPath + " is not file"); return false; } //ffmpeg -i FILE_NAME.flv -ar 22050 NEW_FILE_NAME.mp4 List commend = new java.util.ArrayList<>(); commend.add(ffmpegPath); commend.add("-i"); commend.add(sourceVideoPath); commend.add("-ar"); commend.add("22050"); commend.add(targetFolder + targetName + "." + targetExtension); try { exeCommond(commend); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等), 可以先用别的工具(mencoder)转换为avi(ffmpeg能解析的)格式. * @return */ private boolean processAVI() { List commend = new java.util.ArrayList(); String convertTo = targetFolder + fileRealName + ".avi"; commend.add(mencoderPath); commend.add(sourceVideoPath); commend.add("-oac"); commend.add("mp3lame"); commend.add("-lameopts"); commend.add("preset=64"); commend.add("-ovc"); commend.add("xvid"); commend.add("-xvidencopts"); commend.add("bitrate=600"); commend.add("-of"); commend.add("avi"); commend.add("-o"); commend.add(convertTo); // 命令类型:mencoder 1.rmvb -oac mp3lame -lameopts preset=64 -ovc xvid // -xvidencopts bitrate=600 -of avi -o rmvb.avi try { exeCommond(commend); setSourceVideoPath(convertTo); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 执行命令行 * @param commend * @return */ private int exeCommond(List commend) { ProcessBuilder builder = new ProcessBuilder(); String cmd = commend.toString(); System.out.println(cmd); builder.command(commend); builder.redirectErrorStream(true);//合并错误流到普通的流中,便于一起输出 Process p = null; int exitValue = 1; InputStream in = null; try { p = builder.start(); in = p.getInputStream(); while (in.read() != -1);// 用这个来监控命令行的执行,当视频转换完毕,命令行那边的也就没有东西输出了 /*while (exitValue != 0) {// 如果需要看到 ffmpeg 和 mencoder 的输出信息可以使用这种,注释解开就行 try { while (in.available() > 0) { Character c = new Character((char) in.read()); System.out.print(c); } exitValue = p.exitValue(); } catch (IllegalThreadStateException e) { p.waitFor(1, TimeUnit.SECONDS); } }*/ } catch (Exception e) { System.err.println("exeCommond: unexpected exception - " + e.getMessage()); } try { exitValue = p.exitValue(); } catch (IllegalThreadStateException e) { e.printStackTrace(); } p.destroy(); return exitValue; } /** * 检查文件类型 * @return */ private int checkContentType() { String type = sourceVideoPath.substring(sourceVideoPath.lastIndexOf(".") + 1, sourceVideoPath.length()).toLowerCase(); String ffmpegFormat = "avi|mpg|wmv|3gp|mov|mp4|asf|asx|flv"; String mencoderFormat = "wmv9|rm|rmvb"; if (ffmpegFormat.contains(type)) {// ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等) return 0; }else if (mencoderFormat.contains(type)) {// 对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等),可以先用别的工具(mencoder)转换为avi(ffmpeg能解析的)格式 return 1; } return 9; } private String sumTime(long ms) { int ss = 1000; long mi = ss * 60; long hh = mi * 60; long dd = hh * 24; long day = ms / dd; long hour = (ms - day * dd) / hh; long minute = (ms - day * dd - hour * hh) / mi; long second = (ms - day * dd - hour * hh - minute * mi) / ss; long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss; String strDay = day < 10 ? "0" + day + "天" : "" + day + "天"; String strHour = hour < 10 ? "0"+ hour + "小时" : "" + hour + "小时"; String strMinute = minute < 10 ?"0" + minute + "分" : "" + minute + "分"; String strSecond = second < 10 ?"0" + second + "秒" : "" + second + "秒"; String strMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond; strMilliSecond = milliSecond < 100 ? "0" + strMilliSecond + "毫秒" : "" + strMilliSecond + " 毫秒"; return strDay + " " + strHour + ":" + strMinute + ":" + strSecond + " " + strMilliSecond; } private void setSourceVideoPath(String sourceVideoPath) { this.sourceVideoPath = sourceVideoPath; this.videoFile = Paths.get(sourceVideoPath).toFile(); this.videoExist = videoFile.exists() && videoFile.isFile() ? true : false; String fileName = videoFile.getName(); this.fileRealName = fileName.substring(0, fileName.lastIndexOf(".")).toLowerCase(); } // 不知道这样做对不对,但是在实际开发中不可能一直等着当前线程转码完成, // 遇到要使用 mencoder 才能转换的格式的话还要等待, 所以就开条线程咯~~, @Override public void run() { this.convert(); } }

接着来个测试

public class ConverVideoTest {
    
    public static void main(String args[]) {  
        VideoConvertor cv = new VideoConvertor("F:\\ChromeDownload\\666.rmvb"
	        , "F:\\ChromeDownload\\", "666", "mp4", false, false);
        new Thread(cv).start();
    } 
}

对了,如果要看 ffmpeg 支持的格式的话可以在命令行使用 ffmpeg -formats 来看,挺多的,音频视频图片都有

你可能感兴趣的:(Java,ffmpeg,java,音视频)