Java也疯狂-分享利用ffmpeg做视频转换的工具
透过森林 人气:0朋友需要经常将视频统一转换为mp4格式,市面上的工具很多,但是转换的体积、自动化程度等都不好,于是花了一个小时给朋友写了个给予ffmpeg的批量转换工具,功能简单但是很实用,也正好给学习Java的同学一些参考,其中使用到了swing、简单的多线程(基于Timer)、内部类嵌套实现、线程下如何实现多线程数据的显示、windows的一些操作(读取环境变量、调用外部程序等),ffmpeg是一个非常有用的工具,尤其对于开发者而言。程序运行的效果如下
话不多说,直接上完整代码,复制可以运行(需要ffmpeg)(不懂的欢迎加Q联系:849437360):
package com.octopus; import java.awt.FlowLayout; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; /** * 利用FFmpeg实现视频格式批量转化的工具 * * @author hw * */ public class FfmpegConvertUtil { private String lastMessage = "";// 当前最后一条消息 private String currentFile = "";// 当前操作的文件 //转换过程中的消息 private StringBuffer sbMessages = new StringBuffer(); //ffmpeg的实际路径,请在path中配置对应的bin private String ffmpegPath = null; private boolean isRunning = false; public static void main(String[] args) { FfmpegConvertUtil util = new FfmpegConvertUtil(); util.new MainFrame(util); } /** * 处理某个文件夹 */ public void startProcess(String basepath) { if (ffmpegPath == null) { //在path环境变量中搜索ffmpeg程序的路径 String path = System.getenv("path"); String[] paths = path.split(";"); for (String p : paths) { File file = new File(p); File[] files = file.listFiles(); if (files != null && files.length > 0) { for (File f : files) { if (f.getAbsolutePath().toLowerCase().endsWith("ffmpeg.exe")) { ffmpegPath = f.getAbsolutePath(); break; } } } if(ffmpegPath!=null) { //无需再找 break; } } if (ffmpegPath == null) { JOptionPane.showMessageDialog(null, "找不到转换程序,请设置ffmpeg到path环境变量"); } } //使用timer作为多线程的使用方式 Timer timer = new Timer(); ConvertTask task = new ConvertTask(); task.basePath = basepath; timer.schedule(task, 1000, 10000); } public class ConvertTask extends TimerTask { public FfmpegConvertUtil util; public String basePath; @Override public void run() { isRunning = true; int count = 0; List<String> autoGenerateMp4 = findFilesToConvert(basePath); for (String path : autoGenerateMp4) { currentFile = path + "(一共有文件:" + autoGenerateMp4.size() + "剩余文件数:" + (autoGenerateMp4.size() - count)+")"; convertAllOthersToMp4(path); count++; currentFile = ""; } sbMessages.append(basePath + "当前文件夹下面内容已经处理完成\r\n"); isRunning = false; } } /** * 得到需要转换的文件的列表 * * @param base 起始路径 */ public List<String> findFilesToConvert(String base) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sbMessages.append(sdf.format(new Date()) + ":" + base + "文件搜索开始\r\n"); List<File> files = new ArrayList<File>(); List<String> filesToConvert = new ArrayList<String>(); listAllVideoFile(new File(base), files); for (File f : files) { filesToConvert.add(f.getAbsolutePath()); sbMessages.append(sdf.format(new Date()) + ":" + f.getAbsolutePath() + "加入清单\r\n"); } sbMessages.append(sdf.format(new Date()) + ":" + base + "文件搜索结束,共找到需要转换文件" + filesToConvert.size() + "个\r\n"); return filesToConvert; } /** * 将一个视频文件转化为对应的mp4格式 * @param filepath 要转的文件的绝对路径 */ public void convertAllOthersToMp4(String filepath) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); if (filepath == null) { return; } if (filepath.toLowerCase().endsWith(".mp4")) { sbMessages.append("************" + sdf.format(new Date()) + ":" + filepath + "是mp4文件,跳过转换。\r\n"); return; } sbMessages.append("************" + sdf.format(new Date()) + ":" + filepath + "转换开始\r\n"); String path = filepath.substring(0, filepath.lastIndexOf('.')) + ".mp4"; byte[] buff = new byte[4096]; try { String cmd = ffmpegPath + " -y -i \"" + filepath + "\" \"" + path + "\""; Process process = Runtime.getRuntime().exec(cmd); InputStream inputStream = process.getInputStream(); InputStream errorStream = process.getErrorStream(); while (process.isAlive()) { try { Thread.sleep(10); } catch (InterruptedException e) { } int len = inputStream.available() > 0 ? inputStream.read(buff) : 0; if (len > 0) { lastMessage = new String(buff, 0, len); } len = errorStream.available() > 0 ? errorStream.read(buff) : 0; if (len > 0) { lastMessage = new String(buff, 0, len); } } } catch (IOException e) { sbMessages.append("************" + "转换文件:" + path + "执行失败:" + e.getMessage() + "\r\n"); } sbMessages.append("************" + sdf.format(new Date()) + ":" + filepath + "转换开始" + "\r\n"); } /** * 根据后缀名判断是不是视频文件 * @param fileName 要判断的文件名 * @return 是否为视频文件 */ public boolean isVideoFile(String fileName) { String[] videoExts = new String[] { "vob", "wmv", "avi", "rmvb", "mov", "rm" }; for (String ext : videoExts) { if (fileName.endsWith(ext)) { return true; } } return false; } /** * 获取指定文件夹下指定文件类型的所有文件的完整路径 * * @param parent 起始文件夹 * @param files 存放数据的集合 */ public void listAllVideoFile(File parent, List<File> files) { File[] listFiles = parent.listFiles(); if (listFiles != null) { for (File f : listFiles) { if (f.isFile() && isVideoFile(f.getName().toLowerCase())) { files.add(f); } else { listAllVideoFile(f, files); } } } } /** * 窗体 * @author Administrator * */ public class MainFrame extends JFrame { /** * */ private static final long serialVersionUID = 1L; private JButton btnChooseRootDir; private JTextArea txtMessages; private JLabel lblMessage; private JTextField txtCurrentFile; private JLabel lblCurrentFile; private JTextField txtChoosenDir; private JLabel lblChoosenDir; private JButton btnOpenDir; private javax.swing.Timer timerDisplay; public MainFrame(FfmpegConvertUtil util) { //使用代码构建界面 this.getRootPane().setLayout(new FlowLayout(FlowLayout.LEFT, 10, 5)); this.setDefaultCloseOperation(EXIT_ON_CLOSE); btnChooseRootDir = new JButton(" 选择转换文件夹 "); btnChooseRootDir.setSize(200, 30); btnChooseRootDir.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int result = chooser.showOpenDialog(MainFrame.this); if (result == JFileChooser.APPROVE_OPTION) { txtChoosenDir.setText(chooser.getSelectedFile().getAbsolutePath()); util.startProcess(txtChoosenDir.getText()); } } }); btnOpenDir = new JButton(" 打开文件夹 "); btnOpenDir.setSize(200, 30); btnOpenDir.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { Runtime.getRuntime().exec("explorer \"" + txtChoosenDir.getText() + "\""); } catch (IOException e1) { e1.printStackTrace(); } } }); txtCurrentFile = new JTextField(); txtCurrentFile.setColumns(80); txtCurrentFile.setFont(new Font("微软雅黑", Font.PLAIN, 16)); JLabel lblTemp = new JLabel(" " + " "); lblTemp.setFont(new Font("微软雅黑", Font.PLAIN, 16)); lblCurrentFile = new JLabel("当前处理中文件"); lblCurrentFile.setFont(new Font("微软雅黑", Font.PLAIN, 16)); txtChoosenDir = new JTextField(); txtChoosenDir.setColumns(80); txtChoosenDir.setFont(new Font("微软雅黑", Font.PLAIN, 16)); lblChoosenDir = new JLabel("当前处理的文件夹"); lblChoosenDir.setFont(new Font("微软雅黑", Font.PLAIN, 16)); lblMessage = new JLabel("处理消息显示"); lblMessage.setFont(new Font("微软雅黑", Font.PLAIN, 16)); txtMessages = new JTextArea(30, 105); JScrollPane pane = new JScrollPane(txtMessages, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); timerDisplay = new javax.swing.Timer(5000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (isRunning) { if (sbMessages.length() > 8096) { sbMessages.delete(0, lastMessage.length()); } if (lastMessage != null) { sbMessages.append(lastMessage + "\r\n"); lastMessage = null; } txtCurrentFile.setText(currentFile); txtMessages.setText(sbMessages.toString()); } else { } } }); timerDisplay.start(); this.getRootPane().add(btnChooseRootDir); this.getRootPane().add(btnOpenDir); this.getRootPane().add(lblTemp); this.getRootPane().add(lblChoosenDir); this.getRootPane().add(txtChoosenDir); this.getRootPane().add(lblCurrentFile); this.getRootPane().add(txtCurrentFile); this.getRootPane().add(lblMessage); this.getRootPane().add(pane); this.setSize(1200, 800); this.setResizable(false); this.setVisible(true); } } }
加载全部内容