音频文件
链接:https://pan.baidu.com/s/1g4hsl_NFreNsiRzBtKoolA 提取码:73p1
整个MP3 文件结构
ID3V2 | 包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量。 |
Frame | 1. 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 1 | MPEG 2(LSF) | MPEG 2.5(LSF) |
Layer 1 | 384 | 384 | 384 |
Layer 2 | 1152 | 1152 | 1152 |
Layer 3 | 1152 | 576 | 576 |
从头中读取比特率,采样频率和填充的值后可以进行计算,
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 同步信息1 | 0xFF | 正常 |
error_protection CRC校验 | 1 | 不校验 |
layer 层 | 01 | layer 3 |
version 版本 | 11 | MPEG-1 |
sync2 同步信息2 | 111 | 正常 |
extension 保留字 | 0 | 无 |
padding 填充空白字 | 0 | 无 |
sample_rate_index 采样率索引 | 00 | 44.1KHz |
bit_rate_index 位(bit)率索引 | 1110 | 320Kps |
emphasis 强调方式 | 00 | 无 |
original 版权标志 | 1 | 有 |
copyright 原版标志 | 0 | 拷贝 |
mode_extension 扩展模式,仅用于联合立体声 | 10 | ? |
channel_mode 声道模式 | 01 | Joint 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 字节。(注:有可能没有)