最近在写一个支持读取多种音频格式元数据的库(又在造轮子了),研究到了比较古老的 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 < len(all); i++ {
temp := (all[i] & 1) << 7
if i+1 < len(all) {
temp |= all[i+1] >> 1
}
data = append(data, temp)
}
_, err = modified.Write(data)
现在的 frame header 处于这样的位置
00000001 11111111 11110000
然后用 Windows 自带的播放器打开。结果时长直接读取错误,音频无法播放,
怀疑是不是兼容性不行,播放界老大哥 VLC 一定会遵循规范的对不对!
然后上 VLC 发现,VLC 直接崩了,时长都不读取
等于是大家都嫌按 bit 位来定位 frame header 太麻烦,如果不是在 byte 开头就都放弃了。MPEG 出的规范没一个遵守的
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.