研究音频格式时发现了一个关于 MP3 的冷知识, VLC 等各种软件竟然都没有遵循 mp3 规范的?

22次阅读

共计 1104 个字符,预计需要花费 3 分钟才能阅读完成。

最近在写一个支持读取多种音频格式元数据的库(又在造轮子了),研究到了比较古老的 MP3,分享一下

概览一下 MP3 格式,大概是如下结构

| ID3 v2 metadata | frame data | frame data | frame data ... | ID3 V1 metadata |

metadata 用来储存歌手、专辑等信息,然后后面跟一段一段音频帧,就储存了实际的音乐内容

音频帧详细的规范在这里定义 MP3′ Tech – Frame header。简单概述一下大概是如下格式

AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM

开头的 13 个 bit A 代表帧的开始,要求都被填充为 1,并且说明这 13 个 bit 可能出现在数据中的任意位置,当解码器接收到 13 个 1 就要开始尝试解码(此要求是为了方便流式传输中,只要接收到一段音频帧就可以开始播放)

但问题来了,13 也不是 8 的整倍数,编程语言一般最低只能操作 byte,再往下想操作 bit 就要使用位运算来搞动作了,而这个 Frame header 可能以各种奇怪的型状出现,比如:

11111111 11111000	// 正好在两个 byte 起始位置
00111111 11111110	// 在两个 byte 之间
0111111 111111101	// 在两个 byte 之间,但起始位在第二位,并且最后还跟了一个其他的数据位
00011111 11111111	// 在最后一个 byte 的末尾
00000111 11111111 11010101	// 横跨 nmd 三个 byte,并且后面还跟其他数据位

这解析起来难度上天了啊(摔桌子),并且 header 一跨 byte 边界后面的数据位都要跨边界。

看到这里直接崩溃,随感觉这么逆天的东西不会真的有人支持了吧。于是就写了一个小 demo,把一个 mp3 文件整体位移了一位

// 示例代码
all, err := io.ReadAll(origin)
if err != nil {panic(err)
}

data := make([]byte, 0, len(all))
for i := 0; i > 1
	}
	data = append(data, temp)
}

_, err = modified.Write(data)

现在的 frame header 处于这样的位置

00000001 11111111 11110000

然后用 Windows 自带的播放器打开。结果时长直接读取错误,音频无法播放,

怀疑是不是兼容性不行,播放界老大哥 VLC 一定会遵循规范的对不对!

然后上 VLC 发现,VLC 直接崩了,时长都不读取

等于是大家都嫌按 bit 位来定位 frame header 太麻烦,如果不是在 byte 开头就都放弃了。MPEG 出的规范没一个遵守的

正文完
 0