【Audio】MP3音频文件格式结构解析

网上找了一圈,自己做一个总结;方便自己查询~
https://www.cnblogs.com/ranson7zop/p/7655474.html
https://blog.csdn.net/fulinwsuafcie/article/details/8972346
https://blog.csdn.net/xiahouzuoxin/article/details/7849249
https://www.jianshu.com/p/c3f8be205e3f
https://www.jianshu.com/p/58df71a19901

音频文件

链接:https://pan.baidu.com/s/1g4hsl_NFreNsiRzBtKoolA
提取码:73p1

整个MP3 文件结构

ID3V2包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量。
Frame1. MPEG 音频头, 通常大小为4字节.(当Protection bit==0时,帧头后会有16bit=2byte的CRC,此时帧头大小为6字节)
2. 边信息,9/17/32 字节
3.Xing 头, 8-120字节,如果是VBR,多数都有此Xing头,而且只有第一帧有
4.音频数据
Frame
.
.
.
Frame
一系列的帧,个数由文件大小和帧长决定
1. MPEG 音频头, 通常大小为4字节.(当Protection bit==0时,帧头后会有16bit=2byte的CRC,此时帧头大小为6字节)
2. 边信息,9/17/32 字节
3.音频数据
ID3V1包含了作者,作曲,专辑等信息,长度为128BYTE。

1、ID3V2

ID3V2 到现在一共有 4 个版本,但流行的播放软件一般只支持第 3 版,既 ID3v2.3。由于ID3V1 记录在 MP3 文件的末尾,ID3V2 就只好记录在 MP3 文件的首部了(如果有一天发布 ID3V3,真不知道该记录在哪里)。也正是由于这个原因,对 ID3V2 的操作比 ID3V1 要慢。而且 ID3V2 结构比 ID3V1 的结构要复杂得多,但比前者全面且可以伸缩和扩展。
下面就介绍一下 ID3V2.3。
每个 ID3V2.3 的标签都一个标签头若干个标签帧或一个扩展标签头组成。关于曲目的信息如标题、作者等都存放在不同的标签帧中,扩展标签头和标签帧并不是必要的,但每个标签至少要有一个标签帧。标签头和标签帧一起顺序存放在 MP3 文件的首部。

MP3的ID3V2大小

如图从54 49 54 32到选中位置87 65 1a 85处!标签帧一共65个,等于红色框处大小

在这里插入图片描述

(一)、标签头
char Header[3]; /*必须为"ID3"否则认为标签不存在*/
char Ver; /*版本号 ID3V2.3 就记录 3*/
char Revision; /*副版本号此版本记录为 0*/
char Flag; /*存放标志的字节,这个版本只定义了三位,稍后详细解说*/
char Size[4]; /*标签大小,包括标签头的 10 个字节和所有的标签帧的大小*/

注:对这里我有疑惑,因为在实际寻找首帧的过程中,我发现有的 mp3 文件的标签大小是不
包含标签头的,但有的又是包含的,可能是某些 mp3 编码器写标签的 BUG,所以为了兼容
只好认为其是包含的,如果按大小找不到,再向后搜索,直到找到首帧为止。

(1).标志字节

标志字节一般为 0,定义如下:

abc0 0000

a – 表示是否使用 Unsynchronisation(这个单词不知道是什么意思,字典里也没有找到,一般
不设置)
b – 表示是否有扩展头部,一般没有(至少 Winamp 没有记录),所以一般也不设置
c – 表示是否为测试标签(99.99%的标签都不是测试用的啦,所以一般也不设置)

(2).标签大小

一共四个字节,但每个字节只用 7 位,最高位不使用恒为 0。所以格式如下

0xxx xxxx 0xxx xxxx 0xxx xxxx 0xxx xxxx

计算大小时要将 0 去掉,得到一个 28 位的二进制数,就是标签大小(不懂为什么要这样做),计算公式如下:

int total_size;
total_size = (Size[0]&0x7F)*0x200000
+(Size[1]&0x7F)*0x4000
+(Size[2]&0x7F)*0x80
+(Size[3]&0x7F)
(3).例子
char Header[3];49 44 33
char Ver;03
char Revision;00
char Flag;00
char Size[4];00 00 00 41

ID3.3.0;Flag = 0;size = 65(除标签外的标签帧大小);
在这里插入图片描述

(二)、标签帧

每个标签帧都有一个 10 个字节的帧头和至少一个字节的不固定长度的内容组成。它们也是顺序存放在文件中,和标签头和其他的标签帧也没有特殊的字符分隔。得到一个完整的帧的内容只有从帧头中的到内容大小后才能读出,读取时要注意大小,不要将其他帧的内容或帧头读入。
帧头的定义如下:

char FrameID[4]; /*用四个字符标识一个帧,说明其内容,稍后有常用的标识对照表*/
char Size[4]; /*帧内容的大小,不包括帧头,不得小于 1*/
char Flags[2]; /*存放标志,只定义了 6 位,稍后详细解说*/
(1).帧标识

用四个字符标识一个帧,说明一个帧的内容含义,常用的对照如下:

TIT2=标题 表示内容为这首歌的标题,下同
TPE1=作者
TALB=专集
TRCK=音轨 格式:N/M 其中 N 为专集中的第 N首,M 为专集中共 M 首,N 和 M 为 ASCII 码表示的数字 TYER=年代 是用 ASCII 码表示的数字
TCON=类型直接用字符串表示
COMM=备注 格式:“eng\0 备注内容”,其中 eng 表示备注所使用的自然语言

(2).大小

这个可没有标签头的算法那么麻烦,每个字节的 8 位全用,格式如下

xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx

算法如下:

int FSize;
FSize = Size[0]*0x1000000
+Size[1]*0x10000
+Size[2]*0x100
+Size[3];
(3).标志

只定义了 6 位,另外的 10 位为 0,但大部分的情况下 16 位都为 0 就可以了。格式如下:
abc00000 ijk00000
a – 标签保护标志,设置时认为此帧作废
b – 文件保护标志,设置时认为此帧作废
c – 只读标志,设置时认为此帧不能修改(但我没有找到一个软件理会这个标志)
i – 压缩标志,设置时一个字节存放两个 BCD 码表示数字
j – 加密标志(没有见过哪个 MP3 文件的标签用了加密)
k – 组标志,设置时说明此帧和其他的某帧是一组
值得一提的是 winamp 在保存和读取帧内容的时候会在内容前面加个’\0’,并把这个字节计算在帧内容的大小中。
附:帧标识的含义

(4).例子
  • TIT2
char FrameID[4];54 49 54 32
char Size[4];00 00 00 0d
char Flags[2];00 00

size = 13;途中红色框
在这里插入图片描述

  • TALB
    在这里插入图片描述
  • TPE1
    在这里插入图片描述

2、音频数据帧(Frames)

每个帧都有一个帧头Header,长度是4Byte(32bit),帧头后面可能有两个字节的CRC 校验值,这两个字节的是否存在决定于Header 信息的第16bit,为0 则帧头后面无校验,为1 则有校验,校验值长度为2 个字节,紧跟在Header 后面,接着就是帧的实体数据了,格式如下:

(一)、帧头格式

在这里插入图片描述
本图为整合,是从右往左,从高位到低位;
注:从低位开始
0xFF b 111x xxxx 0xXX 0xXX

帧头长4字节,对于固定位率的MP3文件,所有帧的帧头格式一样其数据结构如下(注:此结构要自己定义):从低位到高位

typedef struct frameHeader
{
	unsigned int sync1:8; 					//同步信息1
	
	unsigned int error_protection:1; 		//CRC校验
	unsigned int layer:2; 					//层
	unsigned int version:2; 				//版本
	unsigned int sync2:3; 					//同步信息2
	
	unsigned int extension:1; 				//保留字
	unsigned int padding:1; 				//填充空白字
	unsigned int sample_rate_index:2; 		//采样率索引
	unsigned int bit_rate_index:4; 			//位率索引
	
	unsigned int emphasis:2; 				//强调方式
	unsigned int original:1; 				//版权标志
	unsigned int copyright:1; 				//原版标志
	unsigned int mode_extension:2; 			//扩展模式,仅用于联合立体声
	unsigned int channel_mode:2; 			//声道模式
}FHEADER, *LPHEADER;

这是从别的地方看得,看到有位率索引这块有两个版本;(还没研究过MPEG~)
在这里插入图片描述

(1)、如何计算帧长度
每帧采样数MPEG 1MPEG 2(LSF)MPEG 2.5(LSF)
Layer 1384384384
Layer 2115211521152
Layer 31152576576

从头中读取比特率,采样频率和填充的值后可以进行计算,

LyaerI使用公式:

帧长度(字节) = (( 每帧采样数/ 8 * 比特率bps ) / 采样频率Hz) + 填充 * 4

LyerII和LyaerIII使用公式:

帧长度(字节)= (( 每帧采样数/ 8 * 比特率bps ) / 采样频率Hz ) + 填充

MPEG 1帧长度(字节)= (( 144 * 比特率bps ) / 采样频率Hz ) + 填充
MPEG 2、2.5帧长度(字节)= (( 72* 比特率bps ) / 采样频率Hz ) + 填充
(2)、每帧的持续时间

下面给出计算公式

每帧持续时间(秒) = 每帧采样数 / 采样频率Hz
每帧持续时间(毫秒) = 每帧采样数 / 采样频率KHz

(3)、例子

在这里插入图片描述

FF: 1111 1111
FB: 1111 1011
E0: 1110 0000
64: 0110 0100

名称位值说明
sync1 同步信息10xFF正常
error_protection CRC校验1不校验
layer 层01layer 3
version 版本11MPEG-1
sync2 同步信息2111正常
extension 保留字0
padding 填充空白字0
sample_rate_index 采样率索引0044.1KHz
bit_rate_index 位(bit)率索引1110320Kps
emphasis 强调方式00
original 版权标志1
copyright 原版标志0拷贝
mode_extension 扩展模式,仅用于联合立体声10?
channel_mode 声道模式01Joint Stereo

帧长度 =〉((1152 / 8 * 32000) / 44100) = 1044字节;
每帧持续时间 = 1152 / 44100 = 0.026s =26 ms
从帧头开始,中间是数据,到下一个帧头,刚刚好1044个字节
在这里插入图片描述

(4)、CRC校验

如果帧头的校验位为0,则帧头后就有一个16位的CRC值,这个值是big-endian的值,把这个值和该帧通过计算得出的CRC值进行比较就可以得知该帧是否有效。

(5)、Side Info

在帧头后边是Side Info(姑且称之为通道信息)。对标准的立体声MP3文件来说其长度为32字节。通道信息后面是Scale factor(增益因子)信息。当解码器在读到上述信息后,就可以进行解码了。
在这里插入图片描述

对于mp3来说现在有两种编码方式,一种是CBR,也就是固定位率,固定位率的帧的大小在整个文件中都是是固定的(公式如上所述),只要知道文件总长度,和从第一帧帧头读出的信息,就都可以通过计算得出这个mp3文件的信息,比如总的帧数,总的播放时间等等,要定位到某一帧或某个时间点也很方便,这种编码方式不需要文件头,第一帧开始就是音频数据。另一种是VBR,就是可变位率,VBR是XING公司推出的算法,所以在MP3的FRAME里会有“Xing"这个关键字(也有用"Info"来标识的,现在很多流行的小软件也可以进行VBR压缩,它们是否遵守这个约定,那就不得而知了),它存放在MP3文件中的第一个有效帧的数据区里,它标识了这个MP3文件是VBR的。同时第一个帧里存放了MP3文件的帧的总个数,这就很容易获得了播放总时间,同时还有100个字节存放了播放总时间的100个时间分段的帧索引,假设4分钟的MP3歌曲,240S,分成100段,每两个相邻INDEX的时间差就是2.4S,所以通过这个INDEX,只要前后处理少数的FRAME,就能快速找出我们需要快进的帧头。其实这第一帧就相当于文件头了。不过现在有些编码器在编码CBR文件时也像VBR那样将信息记入第一帧,比如著名的lame,它使用"Info"来做CBR的标记。

在这里插入图片描述

经过对miniMP3的解析,发下是如下这样的,可能还是有些问题(请各位大佬指正)

在这里插入图片描述

在这里插入图片描述

(6)、VBR头

大部分可变比特率编码的文件都会包含这个头。这个头位于第一个音频帧头之后的某个位置(后面会有具体介绍)。包含XING头的整个第一帧没有音频数据,因此,即使解码器不认识XING头,也可以解码该文件。
在Layer III文件中,XING头紧接着边信息之后。因此,你可以通过使第一帧帧头起始地址加上帧头大小(4个字节),然后再加上边信息大小(参考表2.2.1),就可以得到XING头的位置。虽然Layer III有边信息,但是Layer I、II、III都不用考虑16比特位CRC(如果有的话)。

XING头起始位置 = MPEG第一帧帧头起始位置 + 帧头大小 + 边信息大小。

帧头大小 = 4(或6,当Protection bit==0时,帧头后会有16bit=2byte的CRC)。
为了读出这个头,你必须找到第一个MPEG音频帧头,然后去定位XING头的起始位置。XING头的格式如下:(请注意,位置是从零开始的。位置,长度和例子是以字节格式)下面表格是XING 头格式

在这里插入图片描述
根据上面的格式说明,一个XING头必须至少包含ID字段和Flags字段,其余的字段是依靠与Flags字段的,并且是可选的。在一些情况下,CBR文件中也会包含这个头,在这种情况下,ID值一般用”Info”来标识
这里存在关于XING头的LAME扩展,它是由公同的LAME编码器来使用的,但我并没有过多考虑这一点,因为它不是必需的计算播放时长的因素。这里是为信息标签的MP3文件链接
例子
第二个变量是Big-Endian
120+36=156
在这里插入图片描述

3、ID3v1

ID3v1 标签用来描述 MPEG 音频文件。包含艺术家,标题,唱片集,发布年代和流派。另外还有额外的注释空间。位于音频文件的最后固定为 128 字节。(注:有可能没有)
在这里插入图片描述
在这里插入图片描述

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值