研究音频格式时发现了一个关于 MP3 的冷知识, VLC 等各种软件竟然都没有遵循 mp3 规范的? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
Misakas
V2EX    程序员

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

  •  3
     
  •   Misakas
    Mitsuha 2024-05-08 18:14:45 +08:00 3350 次点击
    这是一个创建于 569 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在写一个支持读取多种音频格式元数据的库(又在造轮子了),研究到了比较古老的 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 出的规范没一个遵守的

    13 条回复    2024-05-09 08:58:02 +08:00
    weyou
        1
    weyou  
       2024-05-08 18:30:05 +08:00 via Android   1
    所谓的流式传输一般不是指比特流,都是字节流。所以只有第一种是符合的
    013231
        2
    013231  
       2024-05-08 18:34:40 +08:00   1
    我认为你理解错了。

    “The frame header itself is 32 bits (4 bytes) length. The first twelve bits (or first eleven bits in the case of the MPEG 2.5 extension) of a frame header are always set to 1 and are called "frame sync". ”

    frame sync 是 11 或 12 位,而且它们必须出现在 frame header 的开头,不能是任意位置。
    Misakas
        3
    Misakas  
    OP
       2024-05-08 18:41:12 +08:00
    @weyou 忽略这个问题了,所以实际上也不会出现跨越边界的情况
    Misakas
        4
    Misakas  
    OP
       2024-05-08 18:47:46 +08:00
    @013231 我知道他必须出现在开头,但 frame header 前面还有 ID3 tag ,我怕 ID3 tag 的数据不是 8 的倍数,导致 后面跟的 frame header 跨 byte 边界。不过 #1 的老哥也说了一般都是按字节流算, 所以我确实理解有问题。
    mercury233
        5
    mercury233  
       2024-05-08 18:48:07 +08:00
    应用层处理的不太可能有比特流吧
    subframe75361
        6
    subframe75361  
       2024-05-08 18:57:51 +08:00 via Android
    好巧,我也在造元数据的轮子,不过是巨人肩膀上的小草 https://github.com/subframe7536/music-metadata-wasm
    0xD800
        7
    0xD800  
       2024-05-08 19:55:29 +08:00
    支持 silk 吗 把 silk 转 pcm 的
    Donaldo
        8
    Donaldo  
       2024-05-08 20:04:27 +08:00
    @Misakas #3 感觉哪怕真的是比特流也没什么大问题,只需要一个缓冲区就好了。
    0o0O0o0O0o
        9
    0o0O0o0O0o  
       2024-05-08 20:11:07 +08:00
    前段时间做个音频流相关的玩具,一开始用 mp3 ,最后还是换成了 ogg ,感觉 mp3 对编程太不友好了
    nightwitch
        10
    nightwitch  
       2024-05-08 21:03:37 +08:00
    借用 Linus 的一句名言,"Standards are paper. I use paper to wipe my butt every day. That's how much that paper is worth."
    mayli
        11
    mayli  
       2024-05-08 22:31:08 +08:00   2
    @Misakas 一般不会不是 8 的倍数,程序员不会自己没事找事。
    cppgohan
        12
    cppgohan  
       2024-05-08 22:40:06 +08:00
    感觉是楼主想多了? 看你发的链接, 我的理解就是直接按 frame head 本身去解就行了. 没有什么 8bit 对齐的问题, 直接解 32 位 frame head 然后你就知道这个 frameblock 哪里结束, 直接处理就好了.

    这个 "frame sync" 在我看来对于应用层, 只是一个 magic number.
    cslive
        13
    cslive  
       2024-05-09 08:58:02 +08:00
    你想多了,你去 id3tag 那个网站,上面列举了好多库,java,go ,c 的都有,找一个你喜欢的看看别人怎么理解规范的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2800 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 75ms UTC 11:04 PVG 19:04 LAX 03:04 JFK 06:04
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86