使用ffmpeg剪辑视频【删除视频头部,尾部,中间,视频拼接,获取视频指定时间截图】
引入pom
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.5</version>
</dependency>
相关java实现 【部分私有类未添加,时间格式转换类未添加,不影响主题逻辑】
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bytedeco.ffmpeg.ffmpeg;
import org.bytedeco.javacpp.Loader;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class VideoUtils {
private static final Logger logger = LogManager.getLogger(VideoUtils.class);
private ProcessBuilder builder;
private String originFilePath;
private String suffix;
public static final String SPOT = ".";
public static final String TEMP_FOLDER = Constants.RESOURCE + File.separator + "temp" + File.separator;
public static final Pattern TIME_PATTERN = Pattern.compile("^\\s*Duration: (\\d\\d):(\\d\\d):(\\d\\d)\\.(\\d).*$", Pattern.CASE_INSENSITIVE);
public static File screenshot(String path, int shotTime) {
File tempFile = FileUtil.createNewFile(TEMP_FOLDER + UUIDUtil.generateUUID() + SPOT + "png");
try {
new ProcessBuilder(Loader.load(ffmpeg.class), "-y", "-i", path, "-ss", String.valueOf(shotTime), "-vframes", "1", tempFile.getAbsolutePath()).start().waitFor();
} catch (Exception e) {
logger.info("获取指定视频指定秒数截图异常, path:{}, shotTime:{}", path, shotTime);
}
return tempFile;
}
public static int getVideoplayTimes(String path) {
try {
InputStream errorStream = new ProcessBuilder(Loader.load(ffmpeg.class), "-y", "-i", path).start().getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = TIME_PATTERN.matcher(line);
if (matcher.matches()) {
int hours = Integer.parseInt(matcher.group(1));
int minutes = Integer.parseInt(matcher.group(2));
int seconds = Integer.parseInt(matcher.group(3));
return hours * 60 * 60 + minutes * 60 + seconds;
}
}
} catch (Exception e) {
logger.error("获取视频时长异常, path:{}", path, e);
}
return 0;
}
public VideoUtils delHeadFrom(String cutTime) {
builder.command().add("-ss");
builder.command().add(cutTime);
return this;
}
public VideoUtils delHeadFrom(int cutTime) {
builder.command().add("-ss");
builder.command().add(String.valueOf(cutTime));
return this;
}
public VideoUtils delEndFrom(int cutEndTime) {
saveTime(cutEndTime);
return this;
}
public VideoUtils delEndFrom(String cutEndTime) {
saveTime(convertTime2Second(cutEndTime));
return this;
}
public boolean delMidFrom(String delFromTime, String delEndTime, String savePath) {
File videoToStart = null, videoToEnd = null;
try {
if (!(DateUtil.isTimeStr(delFromTime, DateUtil.format7) && DateUtil.isTimeStr(delEndTime, DateUtil.format7))) {
throw new IllegalArgumentException("传入时间格式错误, delFromTime: " + delFromTime + "\t delEndTime: " + delEndTime);
}
videoToStart = FileUtil.createNewFile(TEMP_FOLDER + UUIDUtil.generateUUID() + SPOT + this.suffix);
videoToEnd = FileUtil.createNewFile(TEMP_FOLDER + UUIDUtil.generateUUID() + SPOT + this.suffix);
create(originFilePath).saveTime(convertTime2Second(delFromTime)).outFile(videoToStart.getAbsolutePath()).start().waitFor();
create(originFilePath).delHeadFrom(delEndTime).outFile(videoToEnd.getAbsolutePath()).start().waitFor();
return create(videoToStart.getAbsolutePath()).mergeVideos(videoToEnd.getAbsolutePath()).outFile(savePath).start().waitFor() == 0;
} catch (Exception e) {
logger.error("合并视频失败 delFromTime:{}, delEndTime: {} savePath: {} ", delFromTime, delEndTime, savePath, e);
} finally {
if (videoToStart != null) {
videoToStart.deleteOnExit();
}
if (videoToEnd != null) {
videoToEnd.deleteOnExit();
}
}
return false;
}
public boolean delMidFrom(int delFromTime, int delEndTime, String savePath) {
File videoToStart = null, videoToEnd = null;
try {
videoToStart = FileUtil.createNewFile(TEMP_FOLDER + UUIDUtil.generateUUID() + SPOT + this.suffix);
videoToEnd = FileUtil.createNewFile(TEMP_FOLDER + UUIDUtil.generateUUID() + SPOT + this.suffix);
create(originFilePath).saveTime(delFromTime).outFile(videoToStart.getAbsolutePath()).start().waitFor();
create(originFilePath).delHeadFrom(delEndTime).outFile(videoToEnd.getAbsolutePath()).start().waitFor();
return create(videoToStart.getAbsolutePath()).mergeVideos(videoToEnd.getAbsolutePath()).outFile(savePath).start().waitFor() == 0;
} catch (Exception e) {
logger.error("合并视频失败 delFromTime:{}, delEndTime: {} savePath: {} ", delFromTime, delEndTime, savePath, e);
return false;
} finally {
if (videoToStart != null) {
videoToStart.deleteOnExit();
}
if (videoToEnd != null) {
videoToEnd.deleteOnExit();
}
}
}
public VideoUtils mergeVideos(String... originFiles) {
for (String filePath : originFiles) {
builder.command().add("-i");
builder.command().add(filePath);
}
builder.command().add("-filter_complex");
StringBuffer sb = new StringBuffer("[0:v:0][0:a:0]");
for (int i = 0; i < originFiles.length; i++) {
sb.append("[" + (i + 1) + ":v:0][" + (i + 1) + ":a:0]");
}
sb.append("concat=n=" + (originFiles.length + 1) + ":v=1:a=1[outv][outa]");
builder.command().add(sb.toString());
builder.command().add("-map");
builder.command().add("[outv]");
builder.command().add("-map");
builder.command().add("[outa]");
return this;
}
public VideoUtils saveTime(int saveTime) {
if (saveTime < 1) {
throw new IllegalArgumentException("保存时长不能少于一秒");
}
builder.command().add("-t");
builder.command().add(String.valueOf(saveTime));
return this;
}
public VideoUtils outFile(String filePath) {
if (StringUtils.isBlank(filePath)) {
throw new IllegalArgumentException("输出文件路径不存");
}
builder.command().add(filePath);
return this;
}
private int convertTime2Second(String time) {
if (!DateUtil.isTimeStr(time, DateUtil.format7)) {
throw new IllegalArgumentException("传入时间格式错误, time: " + time);
}
return LocalTime.parse(time, DateTimeFormatter.ofPattern(DateUtil.format7)).toSecondOfDay();
}
public static String getFileSuffix(String filePath) {
int lastSeparator = Math.max(filePath.lastIndexOf("\\"), filePath.lastIndexOf("/"));
int lastPoint = filePath.lastIndexOf(SPOT);
if (lastPoint < 1 || lastPoint < lastSeparator || filePath.length() <= lastPoint) {
throw new IllegalArgumentException("文件路径检测失败, filePath: " + filePath);
}
return filePath.substring(lastPoint + 1);
}
public Process start() {
try {
return builder.start();
} catch (IOException e) {
logger.error("处理视频失败,builder:{}", JSONObject.toJSONString(builder.command()), e);
}
return null;
}
public static VideoUtils create(String path) {
return new VideoUtils()
.setOriginFilePath(path)
.setSuffix(getFileSuffix(path))
.setBuilder(new ProcessBuilder(Loader.load(org.bytedeco.ffmpeg.ffmpeg.class), "-y", "-i", path));
}
public ProcessBuilder getBuilder() {
return builder;
}
private VideoUtils setBuilder(ProcessBuilder builder) {
this.builder = builder;
return this;
}
public String getOriginFilePath() {
return originFilePath;
}
private VideoUtils setOriginFilePath(String originFilePath) {
this.originFilePath = originFilePath;
return this;
}
public String getSuffix() {
return suffix;
}
public VideoUtils setSuffix(String suffix) {
this.suffix = suffix;
return this;
}
}