使用FFmpeg获取音频时长

记于:2024-05-06 晚
地点:浙江省·温州市·家里
天气:多云

背景#

业务需求上需要使用ffmpeg转码并获取其识别的音频时长。

代码#

命令示例:

1
ffmpeg -y -hide_banner -i input.wav output.wav

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[aist#0:0/pcm_s16le @ 0x122f06af0] Guessed Channel Layout: mono
Input #0, wav, from 'input.wav':
Metadata:
encoder : Lavf61.1.100
Duration: 00:00:07.86, bitrate: 256 kb/s
Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Hz, mono, s16, 256 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (pcm_s16le (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, wav, to 'output.wav':
Metadata:
ISFT : Lavf61.1.100
Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Hz, mono, s16, 256 kb/s
Metadata:
encoder : Lavc61.3.100 pcm_s16le
[out#0/wav @ 0x6000002c8180] video:0KiB audio:246KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.031011%
size= 246KiB time=00:00:07.86 bitrate= 256.1kbits/s speed=7.04e+03x

Duration: 00:00:07.86 即为音频时长。

接下来使用Java代码调用 ffmpeg 命令,并解析输出信息获取音频时长。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 转码并返回音频时长
*/
public Double transcoding(String ffmpegPath, String input, String output) {
String info = output + ".info";
FileUtil.touch(info);
String[] ffmpegCommand = {
ffmpegPath,
"-hide_banner",
"-y",
// ... 其他参数
"-i", input, output,
"2>&1 | cat >", info
};
List<String> listCommand = CollUtil.newArrayList(ffmpegCommand);

String finalCommandStr = StrUtil.join(" ", listCommand);
log.info("finalCommandStr: {}", finalCommandStr);

int exitCode = -1;
try {
// ProcessBuilder pb = new ProcessBuilder(listCommand);
// 如果有重定向的逻辑,一定要这样调用!!!
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", String.join(" ", listCommand));
Process process = pb.inheritIO().start();
exitCode = process.waitFor();
process.destroy();
} catch (Exception e) {
e.printStackTrace();
}

log.info("exitCode: {}", exitCode);

// 获取音频时长
Double length = this.getAudioLength(info);
FileUtil.del(info);

return length;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 获取音频时长
*/
private Double getAudioLength(String infoFile) {
Double durationInSeconds = null; // 初始化为 null

// 读取infoFile,提取音频时长信息
List<String> lines = FileUtil.readLines(infoFile, "UTF-8");
for (String line : lines) {
if (line.contains("Duration:")) {
// 提取包含 "Duration:" 的行
String durationLine = line.trim();
String durationPart = durationLine.split("Duration:")[1].trim().split(",")[0].trim();

// 解析时长信息,转换为 Double,精确到毫秒
String[] timeParts = durationPart.split(":");
double hours = Double.parseDouble(timeParts[0]);
double minutes = Double.parseDouble(timeParts[1]);
double seconds = Double.parseDouble(timeParts[2]);
double milliseconds = (hours * 3600 + minutes * 60 + seconds) * 1000;

durationInSeconds = milliseconds / 1000.0; // 转换为秒
}
}
return durationInSeconds;
}

注意:

  • ffmpeg命令的输出信息是输出到标准错误流的,所以将标准错误重定向到标准输出流,然后通过管道配合cat命令将标准输出流输出到文件中;(尝试过使用process.getOutputStream()和process.getErrorStream(),但是无法获取到输出信息,所以使用了重定向的方式)
  • 输出文件的中间目录如果不存在,需要提前创建;

补充(2024-05-20):ffprobe命令专门用于检测多媒体文件的信息。