读书频道 > 网站 > 网页设计 > 想到做到:Android开发关键技术与精彩案例
9.2.4 MP3帧结构
12-08-06    叶孤城
收藏    我要投稿   
这不是一本只讲android开发技术的图书,本书让开发者站在移动互联产业链条上思考自己该怎么做本书按照入门篇、进阶篇和游戏篇三大部分,结合37个案例系统全面地介绍Android应用和游戏开发的知识。结合实践和设计模...立即去当当网订购
介于ID3V2和ID3V1之间的部分称做MP3帧,这些帧构成了MP3的音频部分。每个MP3帧由帧头和数据块组成,之间还可能包含2个字节的CRC校验位。校验位是否存在依赖于帧头的第16比特位的值。以比特率为区分标准,MP3可以分为可变比特率和不变比特率两种格式。比特率代表每秒的数据量,一般单位是kb/s。比特率越高,MP3的音质越好,但是文件也越大。每个MP3帧的固定时长为26ms,因此可变比特率的帧大小可能是不同的,而不变比特率的帧大小是固定的,只要分析了第1个帧的大小就可以知道后面帧的大小。
帧头长度是4个字节,也就是32比特,其布局如下所示(每个比特的意义在表9-2中做了详细的介绍)。
AAAAAAAA  AAABBCCD  EEEEFFGH  IIJJKLMM
表9-2  帧头的比特描述
标    识 长    度 位    置 描    述
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 比特率索引值,根据表9-3中的内容可以查询比特率的值,单位是kb/s
F 2 11~10 抽样率索引值,根据表9-4中的内容可以查询抽样率的值,单位是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 目前此标志位很少使用
 
表9-3  比特率索引表(单位:kb/s)
比  特  位 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
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
 
表9-4  抽样率索引(单位:Hz)
比  特  位 MPEG 1 MPEG 2 MPEG 2.5
00 44 100 22 050 11 205
01 48 000 24 000 12 000
10 32 000 16 000 8 000
11 0 0 0
 
MP3帧体的大小由MPEG版本号、比特率、抽样率和填充位4个因素确定。计算公式为:
帧大小= ((MPEG版本号== 1?144:72) * 比特率)/抽样率+ 填充位
解析MP3帧是较复杂的,且直接关系到后面分割MP3文件的工作。对于不变比特率的情况比较简单,不需要完全解析整个MP3文件就可以知道帧数、帧的大小等信息。但是,对于可变比特率的情况就显得比较复杂了,必须逐个分析MP3帧才能确定帧的大小,也只有分析了整个MP3文件才能确定帧的数量。为了能兼顾可变和不变比特率两种情况,我们考虑解析整个MP3文件,然后把每个帧的大小和在文件中的位移存储在一个Vector中,这样就可以通过时间来定位到帧的位置,便于切割MP3文件。通常一个MP3文件可能包含10 000多个帧,如果所有帧都存储在Vector中,将消耗很大的内存空间,且Vector中的元素越多,查询的速度也就越慢。为了优化程序,把10个帧作为一个大帧存储在Vector中,这样在切割时依然可以精确到260ms,甚至还可以把20个帧作为一个整体,这样的效率会更高一些,内存使用更少一些,只是会丧失一些切割的精度。
Frames类的构造器中包含了MP3File类型的参数,这样可以方便获得MP3帧的起始位置。Frames类的源代码如下所示:
public class Frames {
 
    private static int version;
    private static int layer;
    private MP3File file;
    //存储帧在文件中的位移和大小
    private Vector<F> v = new Vector<F>();
 
    public Frames(MP3File file) throws MP3Exception {
        //引用MP3File,方便获得MP3帧开始的位置
        this.file = file;
        try {
            FileInputStream fis = new FileInputStream(file.getPath());
            //定位到帧起始位置,开始解析
            fis.skip(file.getFrameOffset());
            parse(fis);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
 
    //将传入的媒体时间转换为在文件中的位置
    public long time2offset(long time) {
        long offset = -1;
        long index = time / 260;
        offset = ((F) v.get((int) index)).offset;
        return offset;
    }
 
    private void parse(InputStream is) throws MP3Exception {
        try {
            int position = file.getFrameOffset();
            //帧的结束位置,也就是ID3V1的起始位置
            long count = file.getLength() - 128;
            //计算帧的个数,每10个帧放入到Vector中
            int fc = 0;
            //存储10个帧的大小
            int fs = 0;
            while (is.available() > 0 && position < count) {
                //同步帧头位置
                int first = is.read();
                while (first != 255 && first != -1) {
                    first = is.read();
                }
                int second = is.read();
                if (second > 224) {
                    int third = is.read();
                    int forth = is.read();
 
                    int i20 = getBit(second, 4);
                    int i19 = getBit(second, 3);
                    if (i20 == 0 & i19 == 0)
                        throw new MP3Exception("MPEG 2.5 is not supported");
                    //获得MPEG版本号
                    version = i19 == 0 ? 2 : 1;
 
                    int i18 = getBit(second, 2);
                    int i17 = getBit(second, 1);
                    layer = (4 - ((i18 << 1) + i17));
 
                    int i16 = getBit(second, 0);
 
                    int i15 = getBit(third, 7);
                    int i14 = getBit(third, 6);
                    int i13 = getBit(third, 5);
                    int i12 = getBit(third, 4);
                    //查表获得比特率
                    int bitRate = convertBitrate(i15, i14, i13, i12) * 1000;
 
                    int i11 = getBit(third, 3);
                    int i10 = getBit(third, 2);
                    //查表获得抽样率
                    int sampleRate = convertSamplerate(i11, i10);
 
                    int padding = getBit(third, 1);
                    //计算帧的大小
                    int size = ((version == 1 ? 144 : 72) * bitRate)
                            / sampleRate + padding;
                    is.skip(size - 4);
                    fs += size;
                    fc++;
                    if (fc == 10) {
                        //每10帧存储一次
                        F f = new F(position, fs);
                        v.add(f);
                        fc = 0;
                        fs = 0;
                    }
                    position = position + size;
                }
            }
            //将剩余的帧放入Vector中
            if (fs != 0) {
                v.add(new F(position, fs));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //根据表9-4计算抽样率
    protected int convertSamplerate(int in1, int in2) {
        int sample = 0;
        switch ((in1 << 1) | in2) {
        case 0:
            sample = 44100;
            break;
        case 1:
            sample = 48000;
            break;
        case 2:
            sample = 32000;
            break;
        case 3:
            sample = 0;
            break;
        }
        if (version == 1) {
            return sample;
        } else {
            return sample / 2;
        }
    }
    //根据表9-3计算比特率
    protected int convertBitrate(int in1, int in2, int in3, int in4) {
        int[][] convert = { { 0, 0, 0, 0, 0, 0 }, { 32, 32, 32, 32, 32, 8 },
                { 64, 48, 40, 64, 48, 16 }, { 96, 56, 48, 96, 56, 24 },
                { 128, 64, 56, 128, 64, 32 }, { 160, 80, 64, 160, 80, 64 },
                { 192, 96, 80, 192, 96, 80 }, { 224, 112, 96, 224, 112, 56 },
                { 256, 128, 112, 256, 128, 64 },
                { 288, 160, 128, 288, 160, 128 },
                { 320, 192, 160, 320, 192, 160 },
                { 352, 224, 192, 352, 224, 112 },
                { 384, 256, 224, 384, 256, 128 },
                { 416, 320, 256, 416, 320, 256 },
                { 448, 384, 320, 448, 384, 320 }, { 0, 0, 0, 0, 0, 0 } };
        int index1 = (in1 << 3) | (in2 << 2) | (in3 << 1) | in4;
        int index2 = (version - 1) * 3 + layer - 1;
        return convert[index1][index2];
    }
 
    private int getBit(int input, int bit) {
        return (input & (1 << bit)) > 0 ? 1 : 0;
    }
 
    class F {
        int offset;
        int size;
        public F(int _offset, int _size) {
            offset = _offset;
            size = _size;
        }
    }
}
点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.3 功能
下一篇:1.5 小结
相关文章
图文推荐
JavaScript网页动画设
1.9 响应式
1.8 登陆页式
1.7 主题式
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站