简介
本文基于ffmpeg获取音视频时长及对音视频进行拼接。
Java项目
依赖
<dependency>
<groupId>ws.schild</groupId>
<artifactId>jave-all-deps</artifactId>
<version>3.3.1</version>
</dependency>
获取音视频基础信息
public static MultimediaInfo parseInfo(String filePath) {
File media = new File(filePath);
MultimediaObject multimediaObject = new MultimediaObject(media);
MultimediaInfo info = null;
try {
return multimediaObject.getInfo();
} catch (EncoderException e) {
e.printStackTrace();
}
return null;
}
只对mp3、mp4进行了验证,其他格式文件请自行测试。
返回信息
{
"audio": {
"bitRate": 64000,
"channels": 2,
"decoder": "mp3",
"metadata": { },
"samplingRate": 22050
},
"duration": 7630,
"format": "mp3",
"metadata": {
"date": "2015-02-04T12:55:32",
"artist": "Bauchamp",
"album_artist": "Bauchamp",
"genre": "Electronic",
"track": "0",
"encoder": "Lavf58.23.101"
}
}
其中duration
就是音视频时长,单位毫秒。
音频拼接
ffmpeg拼接音频支持两种方式。
1. 直接进行文件拼接
原始命令
ffmpeg64 -i "concat:1.mp3|2.mp3" -acodec copy output.mp3
拼接1.mp3和2.mp3,重新编码输出为output.mp3
注意这种方式不会改变原数据格式,只是进行文件拼接。
java实现代码
public static boolean concat(String toMediaPath, String... fromMediaPaths) throws IOException {
// ffmpeg64 -i "concat:1.mp3|2.mp3" -acodec copy output.mp3
File to = new File(toMediaPath);
if (to.exists()) {
to.delete();
}
DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();
ProcessWrapper ffmpeg = locator.createExecutor();
// -i代表输入参数
ffmpeg.addArgument("-i");
// 需要连接到一起的音频文件
String fromMedias = Arrays.stream(fromMediaPaths).collect(Collectors.joining("|"));
ffmpeg.addArgument("concat:" + fromMedias);
// 重新编码并复制到新文件中
ffmpeg.addArgument("-acodec");
ffmpeg.addArgument("copy");
ffmpeg.addArgument(toMediaPath);
ffmpeg.execute();
BufferedReader reader = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()));
String msg = reader.lines().collect(Collectors.joining("\n"));
System.out.println(msg);
File newFile = new File(toMediaPath);
if (newFile.exists()) {
return true;
}
return false;
}
注:
这种方式拼接得音频可能在浏览器上无法完整播放。猜测原因可能是拼接得音频格式不完全统一导致的。
2. 清洗文件格式并重新编码
原始命令
ffmpeg -i input1.mp4 -i input2.webm -i input3.avi -filter_complex '[0:0] [0:1] [1:0] [1:1] [2:0] [2:1] concat=n=3:v=1:a=1 [v] [a]' -map '[v]' -map '[a]' <编码器选项> output.mkv
- FFmpeg concat 过滤器会重新编码它们。注意这是有损压缩。
- [0:0][0:1] [1:0][1:1] [2:0][2:1] 分别表示第一个输入文件的视频、音频、第二个输入文件的视频、音频、第三个输入文件的视频、音频。
- concat=n=3:v=1:a=1 表示有三个输入文件,输出一条视频流和一条音频流。[v] [a] 就是得到的视频流和音频流的名字,注意在 bash 等 shell 中需要用引号,防止通配符扩展。
java实现代码
public static boolean concat(String toMediaPath, String... fromMediaPaths) throws IOException {
// ffmpeg -i input1.mp4 -i input2.webm -i input3.avi -filter_complex '[0:0] [0:1] [1:0] [1:1] [2:0] [2:1] concat=n=3:v=1:a=1 [v] [a]' -map '[v]' -map '[a]' <编码器选项> output.mkv
File to = new File(toMediaPath);
if (to.exists()) {
to.delete();
}
DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();
ProcessWrapper ffmpeg = locator.createExecutor();
for (String from : fromMediaPaths) {
ffmpeg.addArgument("-i");
ffmpeg.addArgument(from);
}
ffmpeg.addArgument("-filter_complex");
//ffmpeg.addArgument("\"[0:a] [1:a] [2:a] concat=n=3:v=0:a=1 [out]\"");
ffmpeg.addArgument("[0:a] [1:a] [2:a] concat=n=3:v=0:a=2 [out]");
ffmpeg.addArgument("-map");
//ffmpeg.addArgument("\"[out]\"");
ffmpeg.addArgument("[out]");
ffmpeg.addArgument(toMediaPath);
ffmpeg.execute();
BufferedReader reader = new BufferedReader(new InputStreamReader(ffmpeg.getErrorStream()));
String msg = reader.lines().collect(Collectors.joining("\n"));
log.info("[concat] msg: {}", msg);
ffmpeg.close();
File newFile = new File(toMediaPath);
if (newFile.exists()) {
return true;
}
return false;
}
Java代码里只实现了音频得拼接,视频需要自行调整命令实现。
需要注意的是,windows和Linux格式不一样可能导致此方法不能同时运行在两个环境下。