介于ID3V2和ID3V1之间的部分称作MP3帧,这些帧构成了MP3的音频部分。每个MP3帧由帧头和数据块组成,之间还可能包含2个字节的CRC校验位,校验位是否存在依赖于帧头的第16比特位的值。以比特率为区分标准,MP3可以分为可变比特率和不变比特率两种格式。比特率代表每秒钟的数据量,一般单位是kbps。比特率越高,MP3的音质越好,但是文件也越大。每个MP3帧固定时长为26ms,因此可变比特率的帧大小可能是不同的,而不变比特率的帧大小是固定的,只要分析了第1个帧的大小就可以知道后面帧的大小。
帧头长度是4个字节,也就是32比特,其布局如下所示。每个比特的意义在表7-3中做了详细的介绍。
- AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
表7-3 帧头的比特描述
标识 | 长度 | 位置 | 描述 |
A | 11 | 31~21 | 11位的帧同步数据,可以通过查找 同步位来确定帧的起始位置 |
B | 2 | 20~19 | MPEG音频版本号,其中MPEG 2.5 为非官方版本 00 MPEG 2.5 01 保留版本 10 MPEG 2 11 MPEG 1 |
C | 2 | 18~17 | 层(Layer)版本号 00 保留版本号 01 Layer 3 10 Layer 2 11 Layer 1 |
D | 1 | 16 | 保护位,0代表帧头后紧跟2个字 节的CRC校验位;1代表无保护 |
E | 4 | 15~12 | 比特率索引值,根据表7-4中的内容 可以查询比特率的值,单位是kbps |
F | 2 | 11~10 | 抽样率索引值,根据表7-5中的内容 可以查询抽样率的值,单位是Hz |
G | 1 | 9 | 填充位,0代表无填充,1代表有填充。 对于Layer 1,填充位长度为4个字节; Layer 2和Layer 3的填充位 长度为1个字节 |
H | 1 | 8 | 私有标识位 |
I | 2 | 7~6 | 声道模式 00 立体声 01 联合立体声 10 双声道 11 单声道 |
J | 2 | 5~4 | 模式的扩展,只有声道模 式为01时才有意义 |
K | 1 | 3 | 版权标识 |
L | 1 | 2 | 原版标识 |
M | 2 | 1~0 | 目前此标志位很少使用 |
表7-4 比特率索引表(单位:kbps)
比特位 | V1 L1 | V1 L2 | V1 L3 | V2 L1 | V2 L2 | V2 L3 |
0000 | 0 | 0 | 0 | 0 | 0 | 0 |
0001 | 32 | 32 | 32 | 32 | 32 | 8 |
0010 | 64 | 48 | 40 | 64 | 48 | 16 |
0011 | 96 | 56 | 48 | 96 | 56 | 24 |
0100 | 128 | 64 | 56 | 128 | 64 | 32 |
0101 | 160 | 80 | 64 | 160 | 80 | 64 |
0110 | 192 | 96 | 80 | 192 | 96 | 80 |
0111 | 224 | 112 | 96 | 224 | 112 | 56 |
1000 | 256 | 128 | 112 | 256 | 128 | 64 |
续表
比特位 | V1 L1 | V1 L2 | V1 L3 | V2 L1 | V2 L2 | V2 L3 |
1001 | 288 | 160 | 128 | 288 | 160 | 128 |
1010 | 320 | 192 | 160 | 320 | 192 | 160 |
1011 | 352 | 224 | 192 | 352 | 224 | 112 |
1100 | 384 | 256 | 224 | 384 | 256 | 128 |
1101 | 416 | 320 | 256 | 416 | 320 | 256 |
1110 | 448 | 384 | 320 | 448 | 384 | 320 |
1111 | 0 | 0 | 0 | 0 | 0 | 0 |
表7-5 抽样率索引(单位:Hz)
比特位 | MPEG 1 | MPEG 2 | MPEG 2.5 |
00 | 44100 | 22050 | 11205 |
01 | 48000 | 24000 | 12000 |
10 | 32000 | 16000 | 8000 |
11 | 0 | 0 | 0 |
MP3帧体的大小由MPEG版本号、比特率、抽样率和填充位4个因素确定。计算公式为:
帧大小= ((MPEG版本号== 1?144:72) * 比特率)/抽样率 + 填充位
解析MP3帧是较复杂的,且直接关系到后面分割MP3文件的工作。对于不变比特率的情况比较简单,不需要完全解析整个MP3文件就可以知道帧数、帧的大小等信息。但是,对于可变比特率的情况就显得比较复杂了,必须逐个分析MP3帧才能确定帧的大小,也只有分析了整个MP3文件才能确定帧的数量。为了能兼顾可变和不变比特率两种情况,我们考虑解析整个MP3文件,然后把每个帧的大小和在文件中的位移存储在一个Vector中,这样就可以通过时间来定位到帧的位置,便于切割MP3文件。通常一个MP3文件可能包含10000多个帧,如果所有帧都存储在Vector中,将消耗很大的内存空间,且Vector中的元素越多,查询的速度也就越慢。为了优化程序,把10个帧作为一个大帧存储在Vector中,这样在切割时依然可以精确到260ms,甚至还可以把20个帧作为一个整体,这样的效率会更高一些,内存使用更少一些,只是会丧失一些切割的精度。
Frames类的构造器中包含了MP3File类型的参数,这样可以方便获得MP3帧的起始位置。Frames类的源码如下所示:
1. package com.ophone.chapter7_5;
2.
3. import java.io.FileInputStream;
4. import java.io.FileNotFoundException;
5. import java.io.IOException;
6. import java.io.InputStream;
7. import java.util.Vector;
8.
9. public class Frames {
10.
11. private static int version;
12. private static int layer;
13. private MP3File file;
14. //存储帧在文件中的位移和大小
15. private Vector<F> v = new Vector<F>();
16.
17. public Frames(MP3File file) throws MP3Exception {
18. //引用MP3File,方便获得MP3帧开始的位置
19. this.file
20. try {
21. fis = new
22. //定位到帧起始位置,开始解析
23. fis.skip(file.getFrameOffset());
24. parse(fis);
25. } catch (FileNotFoundException e) {
26. e.printStackTrace();
27. } catch (IOException ex) {
28. ex.printStackTrace();
29. }
30. }
31.
32. //将传入的媒体时间转换为在文件中的位置
33. public long time2offset(long time) {
34. offset
35. index = time
36. offset
37. return offset;
38. }
39.
40. private void parse(InputStream is) throws MP3Exception {
41. try {
42. position = file.getFrameOffset();
43. //帧的结束位置,也就是ID3V1的起始位置
44. count = file.getLength() - 128;
45. //计算帧的个数,每10个帧放入到Vector中
46. fc = 0;
47. //存储10个帧的大小
48. fs = 0;
49. > 0 && position < count) {
50. //同步帧头位置
51. first = is.read();
52. while (first != 255 && first != -1) {
53. first = is.read();
54. }
55. second = is.read();
56. >
57. third = is.read();
58. forth = is.read();
59.
60. i20 = getBit(second, 4);
61. i19 = getBit(second, 3);
62. i20 == 0 & i19
63. throw new MP3Exception
("MPEG 2.5 is not supported"); 64. //获得MPEG版本号
65. version = i19
66.
67. i18 = getBit(second, 2);
68. i17 = getBit(second, 1);
69. layer = (4 - ((i18 << 1) + i17));
70.
71. i16 = getBit(second, 0);
72.
73. i15 = getBit(third, 7);
74. i14 = getBit(third, 6);
75. i13 = getBit(third, 5);
76. i12 = getBit(third, 4);
77. //查表获得比特率
78. bitRate = convertBitrate(i15,
i14, i13, i12) * 1000; 79.
80. i11 = getBit(third, 3);
81. i10 = getBit(third, 2);
82. //查表获得抽样率
83. sampleRate = convertSamplerate(i11, i10);
84.
85. padding = getBit(third, 1);
86. //计算帧的大小
87. size = ((version
88. / sampleRate + padding;
89. is.skip(size - 4);
90. fs += size;
91. fc++;
92. fc
93. //每10帧存储一次
94. f = new
95. v.add(f);
96. fc = 0;
97. fs = 0;
98. }
99. positionposition
100. }
101. }
102. //将剩余的帧放入Vector中
103. if (fs != 0) {
104. v.add(new F(position, fs));
105. }
106. } catch (IOException e) {
107. e.printStackTrace();
108. }
109. }
110. //根据表7-5计算抽样率
111. protected int convertSamplerate(int in1, int in2) {
112. sample = 0;
113. switch ((in1 << 1) | in2) {
114. case 0:
115. sample = 44100;
116. break;
117. case 1:
118. sample = 48000;
119. break;
120. case 2:
121. sample = 32000;
122. break;
123. case 3:
124. sample = 0;
125. break;
126. }
127. version
128. return sample;
129. } else {
130. return sample / 2;
131. }
132. }
133. //根据表7-4计算比特率
134. protected int convertBitrate(int in1, int in2, int in3, int in4) {
135. convert
136. { 64, 48, 40, 64, 48, 16 }, { 96, 56, 48, 96, 56, 24 },
137. { 128, 64, 56, 128, 64, 32 }, { 160, 80, 64, 160, 80, 64 },
138. { 192, 96, 80, 192, 96, 80 }, { 224, 112, 96, 224, 112, 56 },
139. { 256, 128, 112, 256, 128, 64 },
140. { 288, 160, 128, 288, 160, 128 },
141. { 320, 192, 160, 320, 192, 160 },
142. { 352, 224, 192, 352, 224, 112 },
143. { 384, 256, 224, 384, 256, 128 },
144. { 416, 320, 256, 416, 320, 256 },
145. { 448, 384, 320, 448, 384, 320 }, { 0, 0, 0, 0, 0, 0 } };
146. index1 = (in1 << 3) | (in2 << 2) | (in3 << 1) | in4;
147. index2
148. return convert[index1][index2];
149. }
150.
151. private int getBit(int input, int bit) {
152. return (input & (1 << bit)) >
153. }
154.
155. class F {
156. int offset;
157. int size;
158. public F(int _offset, int _size) {
159. offset = _offset;
160. size = _size;
161. }
162. }
163. }