如何在服务器端获取MP3文件(VBR或CBR)的真实实际持续时间

30

我曾经使用ffmpeg在服务器端计算MP3文件的持续时间-这似乎运行良好。但今天我发现一些计算是错误的。不知何故,ffmpeg将会错误计算持续时间,且似乎仅出现在可变比特率的mp3文件中。

在本地测试时,我注意到ffmpeg会打印两行额外的绿色文字。

使用的命令:

ffmpeg -i song_9747c077aef8.mp3

ffmpeg说:

[mp3 @ 0x102052600] max_analyze_duration 5000000 reached at 5015510
[mp3 @ 0x102052600] Estimating duration from bitrate, this may be inaccurate

经过一番愉快而热烈的谷歌搜索,我发现了一些相关的帖子,但没有找到解决方案。

然后我尝试增加最大持续时间:

ffmpeg -analyzeduration 999999999 -i song_9747c077aef8.mp3

之后,ffmpeg只返回了第二行:

[mp3 @ 0x102052600] Estimating duration from bitrate, this may be inaccurate

无论哪种情况,所计算的持续时间都是完全错误的。将其与 VLC 进行比较后,我注意到那里的持续时间是正确的。

经过更多的研究,我偶然发现了 mp3info - 我安装并使用了它。

mp3info -p "%S" song_9747c077aef8.mp3

mp3info返回了正确的持续时间,但只是作为一个整数返回,这对我来说不够准确。下面的评论解释了这个问题,用户blahdiblah指出,mp3info只是从文件中提取ID3信息,没有进行任何计算。

我还尝试使用mplayer检索持续时间,但和ffmpeg一样,mplayer也返回了错误的值。


啊,那我想你需要遍历mp3文件,找到每一帧,然后计算出持续时间。 - 1321941
mp3info不是你要找的,它只是从ID3标签和MP3头中提取信息。 - blahdiblah
啊,这个好知道。谢谢你的建议,那我就完全放弃mp3info了。 - SquareCat
你能提供一个ffmpeg获取时长错误的文件,以便我们测试可能的解决方案吗? - blahdiblah
“Estimating”消息可能是虚假的 - http://patches.libav.org/patch/36540/在2013年3月修复了该消息。您是否仍然在最新的ffmpeg中收到此消息?无论如何,如果持续时间不正确,这并没有帮助您。 - Beni Cherniavsky-Paskin
显示剩余7条评论
7个回答

24

我最终使用sox找到了一个合适的解决方案,它返回正确的信息。

sox file.mp3 -n stat
Samples read:          19321344
Length (seconds):    219.062857
Scaled by:         2147483647.0
Maximum amplitude:     1.000000
Minimum amplitude:    -1.000000
Midline amplitude:    -0.000000
Mean    norm:          0.141787
Mean    amplitude:     0.000060
RMS     amplitude:     0.191376
Maximum delta:         0.947598
Minimum delta:         0.000000
Mean    delta:         0.086211
RMS     delta:         0.115971
Rough   frequency:         4253
Volume adjustment:        1.000

长度(秒):219.062857


如果您需要更快的解决方案(似乎sox将从文件中读取所有样本),请查看使用NAudio的解决方案 - 单声道在Linux上可以正常工作,应该是可行的... - Daniel Mošmondor
5
默认情况下,它会输出“sox FAIL formats: no handler for file extension mp3”。我该如何安装它? - shukshin.ivan
2
@shukshin.ivan:如果您需要支持其他格式,您需要安装libsox-fmt-mp3libsox-fmt-all - SDwarfs

20

您可以完全解码文件以获取实际持续时间:

ffmpeg -i input.mp3 -f null -

控制台输出的倒数第二行会显示类似以下内容:

size=N/A time=00:03:49.12 bitrate=N/A

时间time为实际持续时间。在这个例子中,整个过程大约需要0.5秒。


8
扩展自llogan(LordNeckbeard)的解决方案。如果只想获取统计数据,您可以添加标志-v quiet -stats。
ffmpeg -v quiet -stats -i input.mp3 -f null - 

你能把这个转换成秒吗? - John Pollard
不完全清楚提问者是想要一个正确的时间值,还是只是不想显示这个(在某些情况下相对无害的)消息。对于后一种情况,这是一个有用的答案;谢谢! - lindes

8
更简单的方法是使用ffmpeg从具有错误持续时间的ID3标签的文件中复制该文件。这将导致它写入正确的信息。
ffmpeg -i "audio.mp3" -acodec copy "audio_fixed.mp3"

由于使用复制,它只需要原始编码所需时间的一小部分。对于一首歌曲几乎不会注意到,但是在听七个小时的有声读物时,您会真正感激它。重新编码后,ID3“持续时间”标签现在具有正确的信息。


我一直无法弄清楚为什么我的有声读物一直在“编译”,但只使用每章的前几分钟。这就是解决方案。谢谢。 - Pierce

6

ffmpeg在没有提供其他参数时将打印所有文件信息。

使用grepawk仅返回“Duration”:

ffmpeg -i file.mp3 2>&1 | grep Duration

ffmpeg -i file.mp3 2>&1 | awk '/Duration/ { print substr($2,0,length($2)-1) }'


2
ffmpeg 的输出不适合机器解析。请使用 ffprobe 代替。除非您想要使用 ffmpeg 完全解码文件以获取持续时间,但是您的示例并未这样做。 - llogan

1
AV_LOG_FORCE_NOCOLOR=y ffmpeg -nostdin -hide_banner -nostats -loglevel info -i audio.mp3 -f null -vn -c:a copy - 2>&1 | tail -n 2

declare out="$(AV_LOG_FORCE_NOCOLOR=y ffmpeg -nostdin -hide_banner -nostats -loglevel info -i video.mp4 -f null -vn -c:a copy - 2>&1 | tail -n 2 | head -n 1)"
if [[ "$out" =~ \ time=([0-9]+):([0-9]{2}):([0-9]{2})\.([0-9]+) ]]; then
  declare duration=0 us="${BASH_REMATCH[4]}" t
  for t in "${BASH_REMATCH[@]:1:3}"; do
    ((duration *= 60))
    ((duration += ${t#0} ))
  done
  while [ ${#us} -lt 6 ]; do us+=0; done
  ((us >= 500000)) && ((duration++))
  ((duration)) || ((duration++))
fi
echo -E Duration: "$duration"

0
sudo apt install sox 
sudo apt-get install libsox-fmt-mp3

然后:

sox yourfile.mp3 -n stat

看看最受欢迎的答案中第二个评论。我无法对该评论进行评论,所以只能按照我所做的方式去做... - kassbohm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接