Java通過cmd呼叫FFmpeg實現大視訊檔案的分段切割
阿新 • • 發佈:2019-02-12
由於公司業務需要,就用java寫了這麼個小程式,其實挺簡單的,但是也算是弄了半天,所以就發表出來吧~
VideoFileOperate .java
package xyz.leo;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by leo on 2017/2/10.
* 視訊檔案分割操作類
*/
public class VideoFileOperate {
//分割視訊的大小,裝包動作為了避免越界。long應該夠使了。。。
// private long blockSize = 1 * new Long(1024) * 1024 * 1024;
private long blockSize = 400 * 1024 * 1024;
private loadingListener mListener;
private boolean ffmpegWorkingFlag = false;
/**
* 獲取視訊檔案時長
*
* @param file 檔案
* @return 時長 格式hh:MM:ss
* @throws FileNotFoundException 視訊不存在丟擲此異常
*/
private String getVideoTime(File file) throws FileNotFoundException {
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath() + "不存在");
}
List<String> commands = new ArrayList<String>();
commands.add("ffmpeg" );
commands.add("-i");
commands.add(file.getAbsolutePath());
CmdResult result = runCommand(commands);
String msg = result.getMsg();
if (result.isSuccess()) {
//\d{2}:\d{2}:\d{2}
Pattern pattern = Pattern.compile("\\d{2}:\\d{2}:\\d{2}");
Matcher matcher = pattern.matcher(msg);
String time = "";
while (matcher.find()) {
time = matcher.group();
}
return time;
} else {
return "";
}
}
/**
* 獲取檔案大小
*
* @param file 去的檔案長度,單位為位元組b
* @return 檔案長度的位元組數
* @throws FileNotFoundException 檔案未找到異常
*/
private long getVideoFileLength(File file) throws FileNotFoundException {
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath() + "不存在");
}
return file.length();
}
/**
* @param filePath 要處理的檔案路徑
* @return 分割後的檔案路徑
* @throws Exception 檔案
*/
List<String> cutVideo(String filePath) throws Exception {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath + "檔案不存在");
}
if (!filePath.endsWith(".mp4")) {
throw new Exception("檔案格式錯誤");
}
//從ffmpeg獲得的時間長度00:00:00格式
String videoTimeString = getVideoTime(file);
//將時長轉換為秒數
int videoSecond = parseTimeToSecond(videoTimeString);
//視訊檔案的大小
long fileLength = getVideoFileLength(file);
List<String> cutedVideoPaths = new ArrayList<String>();
if (fileLength <= blockSize) {//如果視訊檔案大小不大於預設值,則直接返回原視訊檔案
cutedVideoPaths.add(filePath);
} else {//如果超過預設大小,則需要切割
int partNum = (int) (fileLength / blockSize);//檔案大小除以分塊大小的商
long remainSize = fileLength % blockSize;//餘數
int cutNum;
if (remainSize > 0) {
cutNum = partNum + 1;
} else {
cutNum = partNum;
}
int eachPartTime = videoSecond / cutNum;
List<String> commands = new ArrayList<String>();
String fileFolder = file.getParentFile().getAbsolutePath();
String fileName[] = file.getName().split("\\.");
commands.add("ffmpeg");
for (int i = 0; i < cutNum; i++) {
commands.add("-i");
commands.add(filePath);
commands.add("-ss");
commands.add(parseTimeToString(eachPartTime * i));
if (i != cutNum - 1) {
commands.add("-t");
commands.add(parseTimeToString(eachPartTime));
}
commands.add("-acodec");
commands.add("copy");
commands.add("-vcodec");
commands.add("copy");
commands.add(fileFolder + File.separator + fileName[0] + "_part" + i + "." + fileName[1]);
commands.add("-y");
cutedVideoPaths.add(fileFolder + File.separator + fileName[0] + "_part" + i + "." + fileName[1]);
}
runCommand(commands);
}
return cutedVideoPaths;
}
/**
* 執行Cmd命令方法
*
* @param command 相關命令
* @return 執行結果
*/
private synchronized CmdResult runCommand(List<String> command) {
CmdResult cmdResult = new CmdResult(false, "");
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
try {
Process process = builder.start();
final StringBuilder stringBuilder = new StringBuilder();
final InputStream inputStream = process.getInputStream();
new Thread(new Runnable() {//啟動新執行緒為非同步讀取緩衝器,防止執行緒阻塞
@Override
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
try {
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
// mListener.isLoading(true);
}
// mListener.isLoading(false);
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
process.waitFor();
cmdResult.setSuccess(true);
cmdResult.setMsg(stringBuilder.toString());
} catch (Exception e) {
throw new RuntimeException("ffmpeg執行異常" + e.getMessage());
}
return cmdResult;
}
/**
* 將字串時間格式轉換為整型,以秒為單位
*
* @param timeString 字串時間時長
* @return 時間所對應的秒數
*/
private int parseTimeToSecond(String timeString) {
Pattern pattern = Pattern.compile("\\d{2}:\\d{2}:\\d{2}");
Matcher matcher = pattern.matcher(timeString);
if (!matcher.matches()) {
try {
throw new Exception("時間格式不正確");
} catch (Exception e) {
e.printStackTrace();
}
}
String[] time = timeString.split(":");
return Integer.parseInt(time[0]) * 3600 + Integer.parseInt(time[1]) * 60 + Integer.parseInt(time[2]);
}
/**
* 將秒錶示時長轉為00:00:00格式
*
* @param second 秒數時長
* @return 字串格式時長
*/
private String parseTimeToString(int second) {
int end = second % 60;
int mid = second / 60;
if (mid < 60) {
return mid + ":" + end;
} else if (mid == 60) {
return "1:00:" + end;
} else {
int first = mid / 60;
mid = mid % 60;
return first + ":" + mid + ":" + end;
}
}
interface loadingListener {
void isLoading(boolean loading);
}
// /**
// * 用於判斷ffmpeg是否在工作
// *
// * @return true在工作 暫時無法驗證是否準確
// */
// public boolean isFFmpegWorking() {
//
// mListener = new loadingListener() {
// @Override
// public void isLoading(boolean loading) {
// ffmpegWorkingFlag = loading;
// }
// };
// return ffmpegWorkingFlag;
// }
}
CmdResult.java
這個類是比較雞肋的。。。。本想用於獲得cmd執行後的返回結果,但是使用
ffmpeg -i
這個命令時,雖能取得結果,但是狀態異常,所以現在用處不大。
package xyz.leo;
/**
* Created by leo on 2017/2/10.
* cmd命令執行後結果類
*/
public class CmdResult {
private boolean success;
private String msg;
public CmdResult(boolean success, String msg) {
this.success = success;
this.msg = msg;
}
public CmdResult() {
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}