最近在公司在做视频混剪相关的业务,我也学习了一些相关的知识,这篇文章和大家分享一下获取音频时长的相关工具类

基于jaudiotagger的音频时长解析

为了代码的规范性,我们首先定义一个音频类型的枚举类,用来表明我们的工具支持哪些音频类型。

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import com.mysql.cj.util.StringUtils;

/**
* @author caicai
* @date 2021/8/27 14:08
*/
public enum AudioContentType {
/**
* 非法类型
*/
ILLEGAL("","", 0),
MP3("audio/mp3","MP3",1),
WAV("audio/wav", "WAV",2);
MP3_OTHER("audio/mpeg","MP3",3),
WAV_OTHER("audio/wave", "WAV",4);

private String contentType;
private String type;
private int code;

AudioContentType(String contentType, String type, int code) {
this.contentType = contentType;
this.type = type;
this.code = code;
}

public String getContentType() {
return contentType;
}

public int getCode() {
return code;
}

public String getType() {
return type;
}

public static AudioContentType create(String contentType) {
if (StringUtils.isNullOrEmpty(contentType)) {
return ILLEGAL;
}
for (AudioContentType type : values()) {
if (type.contentType.equals(contentType)) {
return type;
}
}
return ILLEGAL;
}
public static AudioContentType create(int code) {
for (AudioContentType type : values()) {
if (type.code == code) {
return type;
}
}
return ILLEGAL;
}

public static AudioContentType create(Integer code) {
return create(code.intValue());
}
}

然后就是音频工具类的编写了

引入依赖

1
2
3
4
5
6
7
<!-- 获取音频时长 -->
<!-- https://mvnrepository.com/artifact/org/jaudiotagger -->
<dependency>
<groupId>org</groupId>
<artifactId>jaudiotagger</artifactId>
<version>2.0.3</version>
</dependency>

音频工具类

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import com.talkingdata.addct.common.enums.AudioContentType;
import lombok.extern.slf4j.Slf4j;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.mp3.MP3AudioHeader;
import org.jaudiotagger.audio.mp3.MP3File;
import org.springframework.web.multipart.MultipartFile;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
* @author caicai
* @date 2021/8/27 17:36
*/
@Slf4j
public class AudioUtils {

private static final Long TIME_CONVERSION = 1000L;

/**
* 获取音频时长 支持格式:mp3 wav
*/
public static Integer getDuration(File file, AudioContentType contentType) {
switch (contentType) {
case MP3:
case MP3_OTHER:
return getMp3Duration(file).intValue();
case WAV:
case WAV_OTHER:
return getWavDuration(file).intValue();
default:
log.error("不支持的音频格式,无法获取音频时长");
return null;
}
}


/**
* 获取语音文件播放时长(毫秒) 支持wav 格式
*
* @param wavFile
* @return
*/
public static Float getWavDuration(File wavFile) {
BufferedInputStream bufferedInputStream = null;
AudioInputStream audioInputStream = null;
try {
bufferedInputStream = new BufferedInputStream(new FileInputStream(wavFile));
audioInputStream = AudioSystem.getAudioInputStream(bufferedInputStream);
AudioFormat format = audioInputStream.getFormat();
long audioFileLength = wavFile.length();
int frameSize = format.getFrameSize();
float frameRate = format.getFrameRate();
float durationInSeconds = (audioFileLength / (frameSize * frameRate));
return durationInSeconds * TIME_CONVERSION;
} catch (Exception e) {
log.error("音频时长解析错误", e);
return 0f;
} finally {
try {
bufferedInputStream.close();
audioInputStream.close();
} catch (IOException e) {
log.error("字节流关闭错误", e);
}
}

}

/**
* 获取mp3语音文件播放时长(毫秒)
*
* @param mp3File
* @return
*/
public static Float getMp3Duration(File mp3File) {

try {
MP3File f = (MP3File) AudioFileIO.read(mp3File);
MP3AudioHeader audioHeader = (MP3AudioHeader) f.getAudioHeader();
return Float.parseFloat(audioHeader.getPreciseTrackLength() + "") * TIME_CONVERSION;
} catch (Exception e) {
log.error("音频时长解析错误", e);
return 0f;
}
}

/**
* 获取音频名称
*/
public static String getAudioName(MultipartFile audio) {
return audio.getOriginalFilename().split("\\.")[0];
}
}

注意:getWavDuration(File wavFile)方法中AudioSystem.getAudioInputStream(bufferedInputStream);需要用缓冲流。我之前直接用的file,但是这样导致文件一直被占用,无法进行别的操作。

基于ffmpeg的音频时长解析

ffmpeg是一款十分强大的音视频解析工具,巴拉巴拉……

🚧我们进入正题:

引入依赖

1
2
3
4
5
<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg-platform</artifactId>
<version>4.0.2-1.4.3</version>
</dependency>

编写工具类

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
@Slf4j
public class AudioUtils {

private static final Long TIME_CONVERSION = 1000L;

/**
* 获取音频时长
*/
public static Long audioDuration(File file) throws Exception {
Long times = 0L;
FFmpegFrameGrabber ff = null;
try {
ff = new FFmpegFrameGrabber(file);
ff.start();
times = ff.getLengthInTime() / TIME_CONVERSION;
} catch (Exception e) {
log.error("音频时长解析错误", e);
e.printStackTrace();
} finally {
if (ff != null) {
ff.stop();
}
}
return times;
}
}

因为ffmpeg工具十分强大(博主没见过什么世面),上面这个工具类不仅能解析音频时长,还可以解析视频的时长。由于博主电脑里只有.mp3.wav格式的音频和.mp4格式的视频所以只测试这几种格式的文件,时长解析是没有任何问题的。可以大胆上车!!


因为使用的是springboot,文件接收是用的MultipartFile,而我们的工具类传入的参数是File类型,所以我们还需要一个MultipartFile转File的工具类

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/**
* @author wangpeixu
* @date 2021/8/31 14:05
*/
@Slf4j
public class MultipartFileToFileUtils {
/**
* MultipartFile 转 File
*
* @param file
* @throws Exception
*/
public static File multipartFileToFile(MultipartFile file) throws Exception {

File toFile = null;
if (file.equals("") || file.getSize() <= 0) {
file = null;
} else {
InputStream ins = null;
ins = file.getInputStream();
toFile = new File(System.currentTimeMillis()+"_"+file.getOriginalFilename());
inputStreamToFile(ins, toFile);
}
return toFile;
}

//获取流文件
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 删除本地临时文件
* @param file
*/
public static void delteTempFile(File file) {
if (file != null) {
File del = new File(file.toURI());
boolean result = false;
int tryCount = 0;
while(!result && tryCount++ <10)
{
log.info("try to delete file "+ del.getName() +" cnt:"+tryCount);
result = del.delete();
}
}
}
}

总结

上述这两款音频时长解析工具,博主更青睐后者(ffmpeg),真真正正的短小而精悍。代码少,效果好,谁会不爱呢⭐