为什么去除元数据会使我的MP3文件时长减少0.03秒?

我有一个文件,input.mp3,运行ffprobe后得到以下结果:
[mp3 @ 0x559431400a00] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from '../backup/1.mp3':
  Metadata:
    album           : DW01-The Colour Of Magic
    artist          : Terry Pratchett
    album_artist    : Terry Pratchett
    comment         : Read By Nigel Planer
    composer        : Nigel Planer
    genre           : Discworld
    publisher       : Polygram
    title           : Colour Of Magic 1 of 6
    track           : 1
    date            : 1983
    id3v2_priv.WM/Provider: A\x00M\x00G\x00\x00\x00
    id3v2_priv.WM/WMCollectionGroupID: \xe4\xbba\xf0\xf2\xcd.D\xab\x92b\xeb{\x8b\x9a\xce
    id3v2_priv.WM/WMCollectionID: \xe4\xbba\xf0\xf2\xcd.D\xab\x92b\xeb{\x8b\x9a\xce
    id3v2_priv.WM/UniqueFileIdentifier: A\x00M\x00G\x00a\x00_\x00i\x00d\x00=\x00R\x00 \x00 \x00 \x002\x007\x007\x008\x004\x003\x00;\x00A\x00M\x00G\x00p\x00_\x00i\x00d\x00=\x00P\x00 \x00 \x00 \x00 \x00 \x00 \x003\x003\x007\x00;\x00A\x00M\x00G\x00t\x00_\x00i\x00d\x00=\x00T\x00 \x00 \x001\x005\x00
    id3v2_priv.WM/WMContentID: h\x86H@^\xe6qF\x95T\x04m\x01P\x90\x7f
    id3v2_priv.WM/MediaClassSecondaryID: \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
    id3v2_priv.WM/MediaClassPrimaryID: \xbc}`\xd1#\xe3\xe2K\x86\xa1H\xa4*(D\x1e
    id3v2_priv.ZuneCollectionID: \x1d\xcb\xdf\xf3X89D\x81\xd1\xd0s#P\x00\xf8
  Duration: 01:08:19.30, start: 0.000000, bitrate: 32 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 32 kb/s
    Stream #0:1: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 274x417, 90k tbr, 90k tbn, 90k tbc (attached pic)
    Metadata:
      comment         : Cover (front)

我有很多想要对这个文件做的事情,比如将它与其他文件连接起来,然后将其分割成小片段。但是现在,我只考虑简单的情况,即删除元数据,因为仅仅这样就会产生类似于我在进行其他操作时遇到的问题。当我在上述文件上运行以下命令时:

ffmpeg -i input.mp3 -map 0:a -c:a copy -map_metadata -1 output.mp3
然后我在output.mp3上运行ffprobe。
Input #0, mp3, from 'output.mp3':
  Metadata:
    encoder         : Lavf58.29.100
  Duration: 01:08:19.27, start: 0.011995, bitrate: 32 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 32 kb/s
    Metadata:
      encoder         : Lavf
为什么持续时间会减少0.03秒?为什么开始时间不再是0?而且,为什么当我在VLC中打开这个文件时,它似乎难以确定持续时间?对于原始文件,它可以立即知道持续时间。但是对于新文件,它似乎在尝试估计,并且随着播放的进行逐渐接近。 VLC中的编解码器信息(对于两个文件都是相同的)。
Stream 0
    Codec: MPEG Audio layer 1/2 (mpga)
    Type: Audio
    Channels: Stereo
    Sample rate: 44100 Hz
    Bits per sample: 32
    Bitrate: 32 kb/s

1在VLC中,你能否前往“工具”->“编解码器信息”,并将显示的信息粘贴过来? - Mokubai
@Mokubai:正在努力解决这个问题(无法从屏幕上复制/粘贴,所以我需要手动输入) - Benjamin Lindley
1ffprobe的输出的第一行声明了这一点:“从比特率估计持续时间,可能不准确...”知道这是一个估计,这就解释了这个问题。 - Giacomo1968
@Giacomo1968: 除了原始文件外,这个问题只存在于原文件中。VLC在确定确切时间方面没有任何困扰。另外一个相关问题是,为什么第一个文件和第二个文件之间会有这种差异? - Benjamin Lindley
2个回答

MP3文件没有与MP4或MKV文件相同类型的内部结构 -“普通”的MP3只是一系列原始音频帧,并且没有存储其持续时间的地方。对于恒定比特率的MP3文件,可以从文件的长度计算出持续时间,而对于可变比特率,则只能通过逐帧评估整个文件来确定。

也就是说,除非编码器在“LAME INFO tag”中存储了持续时间和VBR信息(又称“Xing VBR tag”)。实际上,几乎所有的编码器和播放器现在都支持此标签,大多数MP3文件都有它。

根据ffprobe的输出,看起来您的原始文件具有这样的标签,而新文件具有(由FFmpeg添加的“encoder: Lavf”是其一部分)。因此,我预期的结果相反 - 新文件应该知道持续时间,但原始文件不知道 - 除非FFmpeg添加了不准确的信息。


事实上,一个普通的MP3文件甚至不会为元数据保留任何空间 - Xing和ID3标签实际上被伪装成特殊的音频帧,大多数解码器知道要忽略它们 - 这也意味着MP3文件的预计持续时间可能取决于它包含或曾经包含的元数据量。如果文件中还有从ID3v2标签删除后剩下的空洞,那么像“大小÷比特率”这样的简单估算将是错误的。 您可以使用Mp3Diags查看文件的结构。(它还可以用于修复不准确的Xing标签,使播放器认为曲目比实际长度长或短。)

1你知道“start”字段是什么意思吗?为什么在删除元数据时,它会变成非零值呢? - Benjamin Lindley
5没有“开始”字段;音频数据从文件的字节0开始的0:00:00。但是正如提到的,ID3v2和Xing标签都被伪装成音频帧,并且似乎ffmpeg自己的时间戳没有考虑到这一点。我猜想在0:00:00.0和0:00:00.011995之间的“音频”数据实际上是ffmpeg本身添加的(新的)Xing标签。(你应该能够在hexdump -C out.mp3中看到它。) - user1686

一个mp3文件包含三个部分:音频块、元数据和垃圾。音频播放器很可能有代码来读取和显示元数据,并且必须有代码来播放音频块。垃圾会被忽略。 现在音频数据的显示长度发生了变化。有两个可能的原因: 1. 元数据编辑器可能破坏了音频块的头部(音频块通常约为30毫秒)。头部被破坏后,音频块就成了垃圾,所以实际上减少了音频长度。 2. 音频播放器并不扫描所有音频块并累加它们的持续时间,而是根据文件大小猜测音频块的数量。也许它没有考虑元数据,所以更多的元数据会导致它高估音频长度。由于你删除了元数据,估计的音频长度减少了。真实长度保持不变,很可能比显示的要少。

8鲜为人知的事实:对于某些歌曲,文件中包含100%的垃圾!(开个玩笑)。 - Luke Sawczak