• OPhone应用开发_MP3帧结构


    介于ID3V2和ID3V1之间的部分称作MP3帧,这些帧构成了MP3的音频部分。每个MP3帧由帧头和数据块组成,之间还可能包含2个字节的CRC校验位,校验位是否存在依赖于帧头的第16比特位的值。以比特率为区分标准,MP3可以分为可变比特率和不变比特率两种格式。比特率代表每秒钟的数据量,一般单位是kbps。比特率越高,MP3的音质越好,但是文件也越大。每个MP3帧固定时长为26ms,因此可变比特率的帧大小可能是不同的,而不变比特率的帧大小是固定的,只要分析了第1个帧的大小就可以知道后面帧的大小。

    帧头长度是4个字节,也就是32比特,其布局如下所示。每个比特的意义在表7-3中做了详细的介绍。

    1. AAAAAAAA  AAABBCCD  EEEEFFGH  IIJJKLMM 

    表7-3  帧头的比特描述

    标识

    长度

    位置

    描述

    A

    11

    3121

    11位的帧同步数据,可以通过查找

    同步位来确定帧的起始位置

    B

    2

    2019

    MPEG音频版本号,其中MPEG 2.5

    为非官方版本

    00  MPEG 2.5

    01  保留版本

    10  MPEG 2

    11  MPEG 1

    C

    2

    1817

    层(Layer)版本号

    00  保留版本号

    01  Layer 3

    10  Layer 2

    11  Layer 1

    D

    1

    16

    保护位,0代表帧头后紧跟2个字

    节的CRC校验位;1代表无保护

    E

    4

    1512

    比特率索引值,根据表7-4中的内容

    可以查询比特率的值,单位是kbps

    F

    2

    1110

    抽样率索引值,根据表7-5中的内容

    可以查询抽样率的值,单位是Hz

    G

    1

    9

    填充位,0代表无填充,1代表有填充。

    对于Layer 1,填充位长度为4个字节;

    Layer 2Layer 3的填充位

    长度为1个字节

    H

    1

    8

    私有标识位

    I

    2

    76

    声道模式

    00  立体声

    01  联合立体声

    10  双声道

    11  单声道

    J

    2

    54

    模式的扩展,只有声道模

    式为01时才有意义

    K

    1

    3

    版权标识

    L

    1

    2

    原版标识

    M

    2

    10

    目前此标志位很少使用

    表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 = file;  
    20.         try {  
    21.             FileInputStream fis = new FileInputStream(file.getPath());  
    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.         long offset = -1;  
    35.         long index = time / 260;  
    36.         offset = ((F) v.get((int) index)).offset;  
    37.         return offset;  
    38.     }  
    39.  
    40.     private void parse(InputStream is) throws MP3Exception {  
    41.         try {  
    42.             int position = file.getFrameOffset();  
    43.             //帧的结束位置,也就是ID3V1的起始位置  
    44.             long count = file.getLength() - 128;  
    45.             //计算帧的个数,每10个帧放入到Vector中  
    46.             int fc = 0;  
    47.             //存储10个帧的大小  
    48.             int fs = 0;  
    49.             while (is.available() > 0 && position < count) {  
    50.                 //同步帧头位置  
    51.                 int first = is.read();  
    52.                 while (first != 255 && first != -1) {  
    53.                     first = is.read();  
    54.                 }  
    55.                 int second = is.read();  
    56.                 if (second > 224) {  
    57.                     int third = is.read();  
    58.                     int forth = is.read();  
    59.  
    60.                     int i20 = getBit(second, 4);  
    61.                     int i19 = getBit(second, 3);  
    62.                     if (i20 == 0 & i19 == 0)  
    63.                         throw new MP3Exception
      ("MPEG 2.5 is not supported");  
    64.                     //获得MPEG版本号  
    65.                     version = i19 == 0 ? 2 : 1;  
    66.  
    67.                     int i18 = getBit(second, 2);  
    68.                     int i17 = getBit(second, 1);  
    69.                     layer = (4 - ((i18 << 1) + i17));  
    70.  
    71.                     int i16 = getBit(second, 0);  
    72.  
    73.                     int i15 = getBit(third, 7);  
    74.                     int i14 = getBit(third, 6);  
    75.                     int i13 = getBit(third, 5);  
    76.                     int i12 = getBit(third, 4);  
    77.                     //查表获得比特率  
    78.                     int bitRate = convertBitrate(i15, 
      i14, i13, i12) * 1000;  
    79.  
    80.                     int i11 = getBit(third, 3);  
    81.                     int i10 = getBit(third, 2);  
    82.                     //查表获得抽样率  
    83.                     int sampleRate = convertSamplerate(i11, i10);  
    84.  
    85.                     int padding = getBit(third, 1);  
    86.                     //计算帧的大小  
    87.                     int size = ((version == 1 ? 144 : 72) * bitRate)  
    88.                             / sampleRate + padding;  
    89.                     is.skip(size - 4);  
    90.                     fs += size;  
    91.                     fc++;  
    92.                     if (fc == 10) {  
    93.                         //每10帧存储一次  
    94.                         F f = new F(position, fs);  
    95.                         v.add(f);  
    96.                         fc = 0;  
    97.                         fs = 0;  
    98.                     }  
    99.                     positionposition = position + size;  
    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.         int 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.         if (version == 1) {  
    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.         int[][] convert = { { 0, 0, 0, 0, 0, 0 }, { 32, 32, 32, 32, 32, 8 },  
    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.         int index1 = (in1 << 3) | (in2 << 2) | (in3 << 1) | in4;  
    147.         int index2 = (version - 1) * 3 + layer - 1;  
    148.         return convert[index1][index2];  
    149.     }  
    150.  
    151.     private int getBit(int input, int bit) {  
    152.         return (input & (1 << bit)) > 0 ? 1 : 0;  
    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.     }  
  • 相关阅读:
    移动端--基于Android Studio的项目文件结构(一)【转】
    java知识体系粗略梳理
    简单的使用hibernate插入数据的例子
    触发器
    存储过程和函数
    视图
    索引的设计和使用
    数据类型的选择
    表类型(存储引擎)的选择
    MySQL中的常用函数
  • 原文地址:https://www.cnblogs.com/xianghang123/p/1705375.html
Copyright © 2020-2023  润新知