以实现如下功能
- 1、支持音频文件转mp3;
- 2、支持视频文件转mp4;
- 3、支持视频提取音频;
- 4、支持视频中提取缩略图;
- 5、支持按时长拆分音频文件;
1、工具类
由于部分原因,没有将FfmpegUtil中的静态的命令行与Type枚举类结合使用。
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/***
*
* @author xuancg
* 要求系统内置ffmpeg工具环境
* @date 2024/9/23
*/
@Slf4j
public class FfmpegUtil {
private static final String CONVERT_MP3 = "ffmpeg -i %s -y %s";
private static final String CONVERT_MP4 = "ffmpeg -i %s -c:v libx264 -c:a copy -y %s";
private static final String EXTRACT_MP3 = "ffmpeg -i %s -q:a 0 -map a -y %s";
private static final String EXTRACT_ICON = "ffmpeg -i %s -ss 0.5 -vframes 1 -r 1 -ac 2 -ab 128k -y -f mjpeg %s";
private static final String SPLIT_AUDIO_BY_SIZE = "ffmpeg -i %s -f segment -segment_time %d -c copy -y %s";
private static final Set<String> MP3_TYPE = new HashSet<>(Arrays.asList("mp3", "wav", "aac", "flac"));
private static final Set<String> MP4_TYPE = new HashSet<>(Arrays.asList("mp4", "avi", "flv", "mpeg", "wmv"));
/***
* 音视频文件格式化,如果存在目标文件会强制覆盖
* 1、支持音频文件转mp3;
* 2、支持视频文件转mp4;
* 3、支持视频提取音频;
* 4、支持视频中提取缩略图;
* 5、支持按时长拆分音频文件;
*/
public static boolean convertMedia(MediaConvertBo convertBo) {
File src = convertBo.getSrc();
File dest = convertBo.getDest();
if (null == src || !src.isFile()) {
log.error("原始文件不存在");
return false;
}
if (null != dest && dest.isFile()) {
log.info("目标文件已存在");
}
long start = System.currentTimeMillis();
Process process = null;
BufferedReader reader = null;
try {
String cmd = createCmd(convertBo);
if(null == cmd){
return false;
}
log.info("ffmpeg执行命令=" + cmd);
// 执行命令
process = Runtime.getRuntime().exec(cmd);
// 获取命令输出结果
reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
while ((line = reader.readLine()) != null) {
log.debug(line);
}
// 明确自己的命令需要执行多长时间,否则可以一直等待
int timeout = convertBo.getTimeout();
if (timeout <= 0) {
process.waitFor();
} else {
process.waitFor(timeout, TimeUnit.SECONDS);
}
return dest.isFile() && dest.length() > 10;
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
log.error("剪裁视频超时source=" + src.getAbsolutePath());
} finally {
if (null != process) {
process.destroy();
}
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
log.error("关闭流失败" + e.getMessage());
}
}
log.info("耗时ms=" + (System.currentTimeMillis() - start));
}
return false;
}
public static boolean isMp4File(File file){
String name = file.getName();
String suffix = name.substring(name.lastIndexOf(".") + 1);
return MP4_TYPE.contains(suffix);
}
public static boolean isMp3File(File file){
String name = file.getName();
String suffix = name.substring(name.lastIndexOf(".") + 1);
return MP3_TYPE.contains(suffix);
}
private static final String createCmd(MediaConvertBo bo) {
File src = bo.getSrc();
String srcPath = src.getAbsolutePath().replace("\\", "/");
String destPath = bo.getDest().getAbsolutePath().replace("\\", "/");;
if (bo.isConvertMp3()) {
if(!isMp3File(src)){
log.error("错误的mp3格式");
return null;
}
return String.format(CONVERT_MP3, srcPath, destPath);
} else if (bo.isConvertMp4()) {
if(!isMp4File(src)){
log.error("错误的mp4格式");
return null;
}
return String.format(CONVERT_MP4, srcPath, destPath);
} else if(bo.getType() == MediaConvertBo.Type.EXTRACT_MP3){
if(!isMp4File(src)){
log.error("错误的mp4格式");
return null;
}
return String.format(EXTRACT_MP3, srcPath, destPath);
} else if(bo.getType() == MediaConvertBo.Type.EXTRACT_ICON) {
if(!isMp4File(src)){
log.error("错误的mp4格式");
return null;
}
return String.format(EXTRACT_ICON, srcPath , destPath);
} else if(bo.getType() == MediaConvertBo.Type.SPLIT_AUDIO_BY_SIZE){
bo.getDest().mkdirs();
String name = src.getName();
String suffix = name.substring(name.lastIndexOf(".") + 1);
// 保持输入输出一致性
return String.format(SPLIT_AUDIO_BY_SIZE, srcPath, bo.getSplitSize(), destPath + "/output_%03d." + suffix);
}
log.error("错误的type");
return null;
}
}
2、入参对象
import lombok.Data;
import java.io.File;
/***
*
* @author xuancg
* @date 2024/9/23
*/
@Data
public class MediaConvertBo {
private File src;
private File dest;
/**0表示持续等待,单位秒*/
private int timeout = 0;
/** 拆分时长,单位秒*/
private int splitSize = 60;
/**处理类型,必传*/
private Type type;
public boolean isConvertMp3(){
return null != type && type == Type.CONVERT_MP3;
}
public boolean isConvertMp4(){
return null != type && type == Type.CONVERT_MP4;
}
public enum Type {
/**将视频转码成mp4*/
CONVERT_MP4,
/**将音频转码成mp3*/
CONVERT_MP3,
/**从视频中提取音频*/
EXTRACT_MP3,
/**从视频中提取缩略图*/
EXTRACT_ICON,
/**按时长拆分音频文件*/
SPLIT_AUDIO_BY_SIZE,
;
}
}
3、junit测试
import org.junit.Test;
import java.io.File;
/***
*
* @author xuancg
* @date 2024/9/23
*/
public class ConvertTest {
/**
* 1分10秒的wav2M大小=转成mp3耗时858ms,200kb大小
*/
@Test
public void convertmp3(){
File src = new File("C:\\Users\\Desktop\\音视频素材\\example.wav");
File dest = new File("C:\\Users\\Desktop\\音视频素材\\example.mp3");
MediaConvertBo bo = new MediaConvertBo();
bo.setType(MediaConvertBo.Type.CONVERT_MP3);
bo.setSrc(src);
bo.setDest(dest);
System.out.println(FfmpegUtil.convertMedia(bo));
}
/**
* 4分13秒视频50M大小=提取音频耗时7秒,4M大小
*/
@Test
public void extractMp3(){
File src = new File("C:\\User\\Desktop\\音视频素材\\202002041032546186.mp4");
File dest = new File("C:\\User\\Desktop\\音视频素材\\202002041032546186.mp3");
MediaConvertBo bo = new MediaConvertBo();
bo.setType(MediaConvertBo.Type.EXTRACT_MP3);
bo.setSrc(src);
bo.setDest(dest);
System.out.println(FfmpegUtil.convertMedia(bo));
}
/**
* 耗时500ms,保持原始视频尺寸
*/
@Test
public void extractIcon(){
File src = new File("C:\\Users\\Desktop\\音视频素材\\202002041032546186.mp4");
File dest = new File("C:\\Users\\Desktop\\音视频素材\\202002041032546186.jpg");
MediaConvertBo bo = new MediaConvertBo();
bo.setType(MediaConvertBo.Type.EXTRACT_ICON);
bo.setSrc(src);
bo.setDest(dest);
System.out.println(FfmpegUtil.convertMedia(bo));
}
/**
* 1分钟2秒
*/
@Test
public void splitAudio(){
File src = new File("C:\\Users\\Desktop\\音视频素材\\example.wav");
File dest = new File("C:\\Users\\Desktop\\音视频素材\\example");
MediaConvertBo bo = new MediaConvertBo();
bo.setType(MediaConvertBo.Type.SPLIT_AUDIO_BY_SIZE);
bo.setSrc(src);
bo.setDest(dest);
bo.setSplitSize(60);
System.out.println(FfmpegUtil.convertMedia(bo));
}
}
5、maven依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <!-- 版本号可替换为最新版本 --> <scope>test</scope> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.5.8</version> </dependency> <!-- 此版本中主要兼容linux和windows系统,如需兼容其他系统平台,请引入对应依赖即可 --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>opencv</artifactId> <version>4.6.0-1.5.8</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>opencv</artifactId> <version>4.6.0-1.5.8</version> <classifier>windows-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>openblas</artifactId> <version>0.3.21-1.5.8</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>openblas</artifactId> <version>0.3.21-1.5.8</version> <classifier>windows-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>5.1.2-1.5.8</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>5.1.2-1.5.8</version> <classifier>windows-x86_64</classifier> </dependency>