1.介绍
这篇文章的目的是讲解MPEG音频帧头的结构(包括XING和VBRI)。并能尽快和精确地计算一个MPEG文件的播放时长。因此不会涉及到关于音频数据的编解码的相关知识。MPEG音频文件构建于layer。最常见的是MPEG-1 Layer III (既MP3),它应用了比较成熟的压缩技术。
2.MPEG音频帧
一个MPEG音频文件是由很多帧数据组成。每一帧包含了一个帧头以及其后的音频数据。同一个文件每一帧的音频数据的采样次数总是相同的。Layer II,II,III的音频帧头都是相同的,不同之处体现在音频数据的编码方式。帧本身是由slot组成的。Layer I的slot大小是4字节,其余情况是1字节。
除了Layer之外,MPEG音频本身也有3个版本,这个几个版本的不同之处体现在能处理的采样率不同(参考 表2.1.2)。MPEG 1 (ISO/IEC 13818-3) 和MPEG 2 (ISO/IEC 11172-3)是ISO标准. MPEG 2.5对MPEG 2进行的非官方的扩展,它是为了支持更低的采样率。MPEG2/2.5 也常被简称为LSF(Low SamplingFrequencies),既低采样率。每个版本的MPEG都有3种不同的Layer。如果你想知道关于MPEG音频文件的更多的技术细节,请参考规范说明。你可以在www.MP3-Tech.org找到规范说明和许多其他关于MPEG有用的信息。
一个文件可以被编码成恒定比特率(CBR)或可变比特率(VBR),这意味着每帧可以有不同的比特率。可变比特率的质量往往比恒定比特率编码的文件更高,因为他们可以在需要的地方使用更高的比特率。
2.1 MPEG音频帧头
帧头位于每帧的开始处,大小通常是32bits(若Protection bit为1,则还要帧头最后添加16bits的校验位),并具有以下格式。帧头中第0位是最高有效位(MSB)。在头位的0是最重要的一点完整的头(最高位)。请注意,位置是从零开始的,位置,长度和示例都是用位格式表示。
起始位置 |
大小 |
解释(作用) |
示例 |
0 |
11 |
帧同步标识,全是1。用于定位帧头起始位置 |
11111111 111 |
11 |
2 |
MPEG音频版本 ID (参考 table 3.2) 00 - MPEG Version 2.5 (unofficial extension of MPEG 2) 01 - reserved 10 - MPEG Version 2 (ISO/IEC 13818-3) 11 - MPEG Version 1 (ISO/IEC 11172-3) |
11 |
13 |
2 |
Layer index 00 - reserved 01 - Layer III 10 - Layer II 11 - Layer I |
01 |
15 |
1 |
Protection bit 0 - protected by 16 bit CRC following header 1 - no CRC |
1 |
16 |
4 |
比特率索引 (参考 table 2.1.3) |
1001 |
20 |
2 |
采样率索引 (参考 table 2.1.2) |
11 |
22 |
1 |
Padding bit If it is set, data is padded with with one slot (important for frame size calculation) Layer I的slot大小是4字节,其余情况是1字节。 |
1 |
23 |
1 |
Private bit (only informative) |
1 |
24 |
2 |
Channel mode 00 - Stereo 01 - Joint Stereo (Stereo) 10 - Dual channel (Two mono channels) 11 - Single channel (Mono)
Note: Dual channel files are made of two independent mono channels. Each one uses exactly half the bitrate of the file. Most decoders output them as stereo, but it might not always be the case. |
01 |
26 |
2 |
Mode extension (Only used in Joint Stereo) (参考 table 2.1.6) |
00 |
28 |
1 |
Copyright bit (only informative) |
1 |
29 |
1 |
Original bit (only informative) |
1 |
30 |
2 |
Emphasis 00 - none 01 - 50/15 ms 10 - reserved 11 - CCIT J.17
The emphasis indication is here to tell the decoder that the file must be de-emphasized, that means the decoder must 're-equalize' the sound after a Dolby-like noise suppression. It is rarely used. |
00 |
2.1.1 MPEG Audio Frame Header
The sampling rate specifies how manysamples per second are recorded. Each MPEG version can handle different samplingrates.
采样率索引 |
MPEG-1 (Hz) |
MPEG-2 (Hz) |
MPEG-2.5 (Hz) |
00 |
44100 |
22050 |
11025 |
01 |
48000 |
24000 |
12000 |
10 |
32000 |
16000 |
8000 |
11 |
reserved |
reserved |
reserved |
2.1.2 MPEG 采样率索引表
比特率的单位是 kbit/s。请注意,这里的kbit/s指的是1000bit/s,而不是1024!比特率指数1111保留,不应该被使用。在MPEG音频标准中有一个自由格式描述。这个自由格式意味着该文件是以恒定比特率编码的,但不是预定义的编码比特率。只有极少数的解码器能够处理这种文件。
Bitrate Index |
MPEG 1 |
MPEG 2, 2.5 (LSF) |
|
|||
Layer I |
Layer II |
Layer III |
Layer I |
Layer II & III |
|
|
0000 |
free |
|
||||
0001 |
32 |
32 |
32 |
32 |
8 |
|
0010 |
64 |
48 |
40 |
48 |
16 |
|
0011 |
96 |
56 |
48 |
56 |
24 |
|
0100 |
128 |
64 |
56 |
64 |
32 |
|
0101 |
160 |
80 |
64 |
80 |
40 |
|
0110 |
192 |
96 |
80 |
96 |
48 |
|
0111 |
224 |
112 |
96 |
112 |
56 |
|
1000 |
256 |
128 |
112 |
128 |
64 |
|
1001 |
288 |
160 |
128 |
144 |
80 |
|
1010 |
320 |
192 |
160 |
160 |
96 |
|
1011 |
352 |
224 |
192 |
176 |
112 |
|
1100 |
384 |
256 |
224 |
192 |
128 |
|
1101 |
416 |
320 |
256 |
224 |
144 |
|
1110 |
448 |
384 |
320 |
256 |
160 |
|
1111 |
reserved |
|
||||
2.1.3 比特率 (kbit/s) |
在MPEG-1 LayerII中,只有某些比特率和某些模式的组合是允许的(如下表)。在MPEG -2/2.5,没有此限制。
比特率 |
允许的模式 |
free |
all |
32 |
single channel |
48 |
single channel |
56 |
single channel |
64 |
all |
80 |
single channel |
96 |
all |
112 |
all |
128 |
all |
160 |
all |
192 |
all |
224 |
stereo, intensity stereo, dual channel |
256 |
stereo, intensity stereo, dual channel |
320 |
stereo, intensity stereo, dual channel |
384 |
stereo, intensity stereo, dual channel |
2.1.4 允许的比特率和模式的组合 |
对于计算帧的大小,你需要每帧的MPEG音频数据的样本数。你可以使用下面的表来获取每帧的MPEG音频数据的样本数:
|
MPEG 1 |
MPEG 2 (LSF) |
MPEG 2.5 (LSF) |
Layer I |
384 |
384 |
384 |
Layer II |
1152 |
1152 |
1152 |
Layer III |
1152 |
576 |
576 |
2.1.5 每帧数据的采样数 |
帧大小的计算公式如下:
帧大小 = ( 每帧采样次数 × 比特率(bit/s) ÷ 8 ÷采样率) + Padding
由于舍入误差,官方公式计算帧的大小是一个有点不同。根据ISO标准,你需要以slot为单位计算帧大小,然后截断这个数字成为整数,之后与槽的大小相乘。你可以在CMPAHeader类中计算帧大小的正确方法。
译者注:在实际项目开发中,对一帧的数据进行处理的时候,缓冲区里存放的数据大小需要比计算出的一帧大小要大一些,比如再多上8个字节。
扩展模式用于加入信息,没有使用的立体声效果,从而减少所需的比特位数据。这些比特位数据是在联合立体模式下编码器动态确定的,每一帧的联合立体模式都可以改变,甚至打开或关上。对于其它的声道模式,扩展模式是无效的。
MPEG音频文件的完整的频率范围被划分成多个子带。共有32个子带。对于Layer I、II,the two bits in the header determine the frequency range (bands)where the intensity stereo is applied。在这个频率范围内,只有一个通道被存储。所有其他的子带包含两个独立声道的信息。对于Layer III,these two bits determine which type of joint stereo is used (intensitystereo and/or M/S stereo)。
Value |
Layer I & II |
Layer III |
|
|
M/S stereo |
Intensity stereo |
|
||
00 |
bands 4 to 31 |
off |
off |
|
01 |
bands 8 to 31 |
off |
on |
|
10 |
bands 12 to 31 |
on |
off |
|
11 |
bands 16 to 31 |
on |
on |
|
2.1.6 扩展模式 |
2.2 CRC校验
若保护位为0,则音频帧中会包含一个16位的CRC(Cyclic Redundancy Checksum)。This checksum directly follows theframe header and is a big-endian WORD. 为了验证CRC,你必须计算音频帧的校验和,并与存储的CRC进行比较。若不等,则可能传输途中数据被损坏。检查CRC也有利于来验证你真的发现了一帧的开始位置,because the sync bits do in same cases also occur within the datasection of a frame.
CRC是采用计算的CRC - 16算法(生成器polynom 0x8005),也是一帧的一部分。下面的数据被认为是CRC:帧头的最后两个字节,和音频数据中一些紧接CRC字段的比特位。当计算CRC的时候,需要跳过校验和字段。不幸的是,在Layer II中没有简单的方法来计算出用于计算校验和的必要帧的数量。
你需要帧头之外的其它信息来计算所需的比特位。但是,从帧头信息中可以计算出在Layer I和Layer III中保护比特位的数量。
对于Layer III,在计算CRC的时候,你需要考虑完整的边信息。
边信息紧接着帧头。它包含了一些解码器会用到的一些信息,用于解码器控制音频流的播放,但不包含实际的音频数据。下表显示了所有Layer III文件的边信息的大小。
|
MPEG 1 |
MPEG 2/2.5 (LSF) |
Stereo, Joint Stereo, Dual Channel |
32 |
17 |
Mono |
17 |
9 |
2.2.1 Layer III 边信息大小 (in bytes) |
对于Layer I的文件,你必须考虑到扩展模式(见表2.1.6)。然后你可以以下公式计算出用于计算CRC的比特位的数量:
4 * ( 声道数 * bound of intensity stereo + (32 - bound of intensity stereo) );
This can be read as two times the number ofstereo subbands plus the number of mono subbands and the result multiplied with4. For simple mono frames, this equals 128, because the number of channels isone and the bound of intensity stereo is 32, meaning that there is no intensitystereo. For stereo frames this is 256. For more information have a look at theCRC code in the class CMPAFrame.
3. CBR
可以通过以下公式可以获取播放时长 (仅适用于恒定码率文件):
播放时长 = ( 文件大小 – ID3大小 ) × 8 ÷ 比特率(bit/s)
4. VBR头
有些文件的编码是可变比特率模式(VBR)的。为了估计这些文件的时间,你必须知道整个文件的平均比特率。它常常与第一帧的码率相差很多,因为最低的比特率可用在音乐沉默时(尤其是在开始时)。要获得这个平均率,你必须通过所有的帧中的文件,并计算出每帧的比特率,相加的总和除以帧数。而这不是一个好的做法(很慢)。在第一帧的音频数据区中存在一个附加的VBR头。它包含了文件中的帧的总数,有了总帧数,我们可以用以下公式计算音频的播放时长:
Duration = 总帧数 * 每帧采样数 / 采样率
而且,VBR头往往会包含一个表,当在文件中寻找位置的时候会利用到这张表。
Because this isn't a good practice (veryslow), there exists additional VBR headers within the data section of the firstframe (after the frame header). They contain the total number of frames in thefile from which you can calculate the duration in seconds with the followingformula:
4.1 XING头
大部分可变比特率编码的文件都会包含这个头。这个头位于第一个音频帧头之后的某个位置(后面会有具体介绍)。包含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头的格式如下:(请注意,位置是从零开始的。位置,长度和例子是以字节格式)
Position |
Length |
Meaning |
Example |
0 |
4 |
VBR header ID in 4 ASCII chars, either 'Xing' or 'Info', not NULL-terminated |
“Xing” |
4 |
4 |
Flags which indicate what fields are present, flags are combined with a logical OR. Field is mandatory. 0x00000001 - Frames field is present |
0x0007 |
8 |
4 |
Number of Frames as Big-Endian DWORD (optional) |
7344 |
8 or 12 |
4 |
Number of Bytes in file as Big-Endian DWORD (optional) |
45000 |
8, 12 or 16 |
100 |
100 TOC entries for seeking as integral BYTE (optional) |
|
8, 12, 16, 108, 112 or 116 |
4 |
Quality indicator as Big-Endian DWORD |
0 |
2.3.1.1 XING 头格式 |
根据上面的格式说明,一个XING头必须至少包含ID字段和Flags字段,其余的字段是依靠与Flags字段的,并且是可选的。在一些情况下,CBR文件中也会包含这个头,在这种情况下,ID值一般用”Info”来标识
这里存在关于XING头的LAME扩展,它是由公同的LAME编码器来使用的,但我并没有过多考虑这一点,因为它不是必需的计算播放时长的因素。这里是为信息标签的MP3文件链接(http://gabriel.mp3-tech.org/mp3infotag.html#versionstring)。
4.2 VBRI 头
据我所知,这个头只存在与用Fraunhofer编码器编码的MPEG音频文件中。它和XING头不同。你可以在第一帧音频帧头之后的32个字节偏移处准确地定位这个头。
VBRI头起始位置 = MPEG第一帧帧头起始位置 + 帧头大小 + 32。
帧头大小 = 4(或6,当Protection bit==0时,帧头后会有16bit=2byte的CRC)。
请注意,位置是从零开始的。位置,长度和例子是字节格式表示。
Position |
Length |
Meaning |
Example |
0 |
4 |
VBR header ID in 4 ASCII chars, always 'VBRI', not NULL-terminated |
'VBRI' |
4 |
2 |
Version ID as Big-Endian WORD |
1 |
6 |
2 |
Delay as Big-Endian float |
7344 |
8 |
2 |
Quality indicator |
75 |
10 |
4 |
Number of Bytes as Big-Endian DWORD |
45000 |
14 |
4 |
Number of Frames as Big-Endian DWORD |
7344 |
18 |
2 |
Number of entries within TOC table as Big-Endian WORD |
100 |
20 |
2 |
Scale factor of TOC table entries as Big-Endian DWORD |
1 |
22 |
2 |
Size per table entry in bytes (max 4) as Big-Endian WORD |
2 |
24 |
2 |
Frames per table entry as Big-Endian WORD |
845 |
26 |
|
TOC entries for seeking as Big-Endian integral. From size per table entry and number of entries, you can calculate the length of this field. |
|
2.3.2.1 VBRI Header |
6.MP3的文件的内容组织结构
所以,总结起来,一般的MP3文件所包含的内容如 下:
[ID3。。。] ID3 V2的头,大多数最新的MP3,都有这个头 [APE 头] 用于APE格式的头,现在也用于MPEG |
第一帧包 含: 1. MPEG 音频头, 通常大小为4字节.(当Protection bit==0时,帧头后会有16bit=2byte的CRC,此时帧头大小为6字节) 2. 边信息,9/17/32 字节 [3.Xing 头] 8-120字节,如果是VBR,多数都有此Xing头,而且只有第一帧有 。。。。。音频数据。。。。。 |
第二帧(帧头,边信息,数据。。。) |
第三帧(帧头,边信息,数据。。。) |
。。。 |
最后一帧(帧头,边信息,数据。。。) |
[TAG 。。。] 128字节的ID3 V1信息,如果没有前面的ID3 V2,多数都有这个ID3 V1的头 |
注:[]号内的,表示,可选,即如果有的话。
表8 MP3文件的内容组织结构
参考资料:
[1] MPEG Audio Frame Header [登陆该页面后,有源码和程序供下载]
http://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx
[2] MPEG简介 + 如何计算CBR_VBRMP3的播放时间
http://blog.163.com/againinput4@yeah/blog/static/12276427120098197322266
附上我自己写的源码,抛砖引玉,欢迎大家友好地讨论 ([email protected]):
/*
* MPEGAudioFrame.h
*
* Created on: 2010-11-26
* Author: [email protected]
*
* 2010-12-16:
* 1)在运行时判断是否是大端字节序
*/
#ifndef MPEGAUDIOFRAME_H_
#define MPEGAUDIOFRAME_H_
// Delete by gansc23 at 2010-12-16 for 在运行时判断是否是大端
/* *
* 大端字节序,若不定义则是小端字节序
*/
//#define BIG_ENDIAN
// MPEGAudioRet::mErrCode
enum {
MPEG_AUDIO_OK = 0
, MPEG_AUDIO_NEED_MORE = - 99
};
/* *
* 代表返回结果的结构体
*/
typedef struct _MPEGAudioRet{
int mErrCode;
int mNextPos;
} MPEGAudioRet;
/* *
* 代表MPEG音频帧的信息
*/
typedef struct _MPEGAudioFrameInfo{
// MPEG Audio Frame Header /
/* *
* MPEG-1.0: 10
* MPEG-2.0: 20
* MPEG-2.5: 25
* invalid : other
*/
int mMPEGVersion : 6 ;
/* *
* Layer I : 1
* Layer II : 2
* Layer III: 3
* invalid : other
*/
int mLayer : 3 ;
/* *
* in kbits/s
*/
int mBitrate : 10 ;
/* *
* in Bytes
*/
int mPaddingSize : 4 ;
/* *
* Channel mode
*
* Joint Stereo (Stereo) - 0
* Single channel (Mono) - 1
* Dual channel (Two mono channels) - 2
* Stereo - 3
*/
int mChannelMode : 3 ;
/* *
* Mode extension, Only used in Joint Stereo Channel mode.
* not process
*/
int mExtensionMode : 3 ;
/* *
* Emphasis:
* The emphasis indication is here to tell the decoder that the file must be
* de-emphasized, that means the decoder must 're-equalize' the sound after
* a Dolby-like noise suppression. It is rarely used.
*
* 0 - none
* 1 - 50/15 ms
* 2 - reserved (invalid)
* 3 - CCIT J.17
*/
int mEmphasis : 3 ;
/* *
* in Hz
*/
int mSamplerate : 17 ;
/* *
* Protection off: 0
* Protection on : 1
*/
int mProtection : 2 ;
/* *
* Copyright bit, only informative
*/
int mCopyrightBit : 2 ;
/* *
* Original bit, only informative
*/
int mOriginalBit : 2 ;
/* *
* 边信息大小(Layer III), in Bytes
*/
int mSideInfoSize : 7 ;
/* *
* Samples per frame
*/
int mSamplesPerFrame : 12 ;
/* *
* 0 - CBR
* 1 - CBR(INFO)
* 2 - VBR(XING)
* 3 - VBR(VBRI)
*/
int mBitrateType : 3 ;
/* *
* 2 Bytes
*/
unsigned short int mCRCValue;
// XING, INFO or VBRI Header /
/* *
* mTotalFrames
*/
int mTotalFrames;
/* *
* mTotalBytes
*/
int mTotalBytes;
/* *
* Quality indicator
*
* From 0 - worst quality to 100 - best quality
*/
short int mQuality;
// only for VBRI Header
/* *
* Size per table entry in bytes (max 4)
*/
short int mEntrySize : 4 ;
/* *
* Number of entries within TOC table
*/
short int mEntriesNumInTOCTable;
/* *
* Scale factor of TOC table entries
*/
short int mTOCTableFactor;
/* *
* VBRI version
*/
short int mVBRIVersionID;
/* *
* Frames per table entry
*/
short int mFramesNumPerTable;
/* *
* VBRI delay
*
* Delay as Big-Endian float.
*/
unsigned char mVBRIDelay[ 2 ];
/// /
/* *
* Frame size
*/
int mFrameSize;
} MPEGAudioFrameInfo;
/* *
* 寻找并下一帧MPEG音频帧的信息。
*
* @param buf 数据区
* @param bufSize 数据区大小
* @param info[out] MPEG音频帧的信息,可以为NULL
* @param firstFrame 是否是寻找和解析第一帧数据
*
* @ret 若MPEGAudioRet.mErrCode为MPEG_AUDIO_OK,则代表解析成功;否则就是需要更多数据。
* 当解析成功的时候,MPEGAudioRet.mNextPos代表了下一帧的偏移量;否则代表当下一次传
* 入更多的数据,应该开始解析的偏移量。
*/
MPEGAudioRet findMpegAudioFramePos(
unsigned char * buf,
int bufSize,
MPEGAudioFrameInfo * info,
bool firstFrame);
#endif /* MPEGAUDIOFRAME_H_ */
/*
* MPEGAudioFrame.cpp
*
* Created on: 2010-11-26
* Author: [email protected]
*
* 2010-12-16:
* 1)It should ignore the CRC data size when finding "XING" header.
* 2)Fix the XING flag bug.
* 3)在运行时判断是否是大端模式
*/
#include < STRING.H >
#include " MPEGAudioFrame.h "
enum {
MPEG_AUDIO_ERR = - 199
};
/* *
* 比特率表 (kbits/s)
*/
const short int BitrateTable[ 2 ][ 3 ][ 15 ] =
{
{
{ 0 , 32 , 64 , 96 , 128 , 160 , 192 , 224 , 256 , 288 , 320 , 352 , 384 , 416 , 448 },
{ 0 , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 , 384 },
{ 0 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 160 , 192 , 224 , 256 , 320 }
},
{
{ 0 , 32 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 , 176 , 192 , 224 , 256 },
{ 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 },
{ 0 , 8 , 16 , 24 , 32 , 40 , 48 , 56 , 64 , 80 , 96 , 112 , 128 , 144 , 160 }
}
};
/* *
* 采样率表
*/
const short int SamplerateTable[ 3 ][ 3 ] =
{
{ 44100 , 48000 , 32000 }, // MPEG-1
{ 22050 , 24000 , 16000 }, // MPEG-2
{ 11025 , 12000 , 8000 } // MPEG-2.5
};
/* *
* 解析MPEG音频帧头
*/
static int parseMpegFrameHdr(
unsigned char * hdrBuf,
int bufSize,
MPEGAudioFrameInfo * info,
bool firstFrame);
/* *
* 解析"XING"或者"INFO"头,只会出现在第一帧音频数据区域中
*/
static int parseVbrXINGHdr(unsigned char * xingHdr, int bufSize,
MPEGAudioFrameInfo * info);
/* *
* 解析"VBRI"头,只会出现在第一帧音频数据区域中
*/
static int parseVbrVBRIHdr(unsigned char * vbriHdr, int bufSize,
MPEGAudioFrameInfo * info);
/* *
* 对Frame info中的数据进行计算
*/
static int calculateFrame(MPEGAudioFrameInfo * info);
/* *
* big-endian to local endian
*/
static int bigendian2Local(unsigned char * valueAddr, int len);
/* *
* little-endian to local endian
*/
static int litendian2Local(unsigned char * valueAddr, int len);
/* *
* swap endian:
*
* little-endian -> big-endian
* big-endian -> little-endian
*/
static void exchangeByteEndian(unsigned char * valueAddr, int len);
/**
* 在运行时判断是否为大端字节序
*/
static inline int isBigendian();
// ==============================================================================
// findMpegAudioFramePos
// ==============================================================================
MPEGAudioRet findMpegAudioFramePos(
unsigned char * buf,
int bufSize,
MPEGAudioFrameInfo * info,
bool firstFrame)
{
MPEGAudioRet ret;
ret.mErrCode = MPEG_AUDIO_NEED_MORE;
ret.mNextPos = 0 ;
MPEGAudioFrameInfo tmp;
MPEGAudioFrameInfo * pFrameInfo = info;
if ( ! pFrameInfo)
pFrameInfo = & tmp;
int loopSize = bufSize - 1 ;
int i = 0 ;
for ( ; i < loopSize; i ++ )
{
// 帧同步标识: 1111 1111 111x xxxxb
if ( buf[i] == 0xff && (buf[i + 1 ] & 0xe0 ) == 0xe0 )
{
memset(pFrameInfo, 0 , sizeof ( * pFrameInfo));
ret.mErrCode = parseMpegFrameHdr(buf + i, bufSize - i, pFrameInfo, firstFrame);
if ( MPEG_AUDIO_OK == ret.mErrCode
|| MPEG_AUDIO_NEED_MORE == ret.mErrCode)
{
break ;
}
}
else if (i == loopSize - 1 && buf[i + 1 ] != 0xff )
{
i ++ ;
}
}
ret.mNextPos = i;
if (i > 0 && ret.mErrCode != MPEG_AUDIO_OK)
memset(pFrameInfo, 0 , sizeof (MPEGAudioFrameInfo));
return ret;
}
// ==============================================================================
// parseMpegFrameHdr
// ==============================================================================
int parseMpegFrameHdr(
unsigned char * hdrBuf,
int bufSize,
MPEGAudioFrameInfo * info,
bool firstFrame)
{
if (bufSize < 4 ) // 帧头至少有4个字节
return MPEG_AUDIO_NEED_MORE;
// Protection Bit
info -> mProtection = (hdrBuf[ 1 ] & 0x01 );
info -> mProtection = ( 0 == info -> mProtection ? 1 : 0 );
if (info -> mProtection && bufSize < 6 )
{
// protected by 16 bit CRC following header
return MPEG_AUDIO_NEED_MORE;
}
// MPEG版本
info -> mMPEGVersion = ((hdrBuf[ 1 ] >> 3 ) & 0x03 );
switch (info -> mMPEGVersion)
{
case 0 :
info -> mMPEGVersion = 25 ;
break ;
case 2 :
info -> mMPEGVersion = 20 ;
break ;
case 3 :
info -> mMPEGVersion = 10 ;
break ;
default :
info -> mMPEGVersion = 0 ;
return MPEG_AUDIO_ERR;
};
// Layer index
info -> mLayer = ((hdrBuf[ 1 ] >> 1 ) & 0x03 );
switch (info -> mLayer)
{
case 1 :
info -> mLayer = 3 ;
break ;
case 2 :
info -> mLayer = 2 ;
break ;
case 3 :
info -> mLayer = 1 ;
break ;
default :
info -> mLayer = 0 ;
return MPEG_AUDIO_ERR;
};
// 比特率
info -> mBitrate = ((hdrBuf[ 2 ] >> 4 ) & 0x0f );
if (info -> mBitrate == 0x0f )
return MPEG_AUDIO_ERR;
unsigned char index_I = (info -> mMPEGVersion == 10 ? 0 : 1 );
unsigned char index_II = info -> mLayer - 1 ;
info -> mBitrate = BitrateTable[index_I][index_II][info -> mBitrate];
// 采样率
info -> mSamplerate = ((hdrBuf[ 2 ] >> 2 ) & 0x03 );
if (info -> mSamplerate == 0x03 )
return MPEG_AUDIO_ERR;
index_I = 2 ; // MPEG-2.5 by default
if (info -> mMPEGVersion == 20 )
index_I = 1 ;
else if (info -> mMPEGVersion == 10 )
index_I = 0 ;
info -> mSamplerate = SamplerateTable[index_I][info -> mSamplerate];
// Padding size
info -> mPaddingSize = ((hdrBuf[ 2 ] >> 1 ) & 0x01 );
if (info -> mPaddingSize)
{
info -> mPaddingSize = ( info -> mLayer == 1 ? 4 : 1 );
}
// channel mode
info -> mChannelMode = ((hdrBuf[ 3 ] >> 6 ) & 0x03 );
switch (info -> mChannelMode)
{
case 0 :
info -> mChannelMode = 3 ;
break ;
case 1 :
info -> mChannelMode = 0 ;
break ;
case 2 :
info -> mChannelMode = 2 ;
break ;
case 3 :
default :
info -> mChannelMode = 1 ;
};
// 在MPEG-1 Layer II中,只有某些比特率和某些模式的组合是允许的。
// 在MPEG -2/2.5,没有此限制。
if (info -> mMPEGVersion == 10 && info -> mLayer == 2 )
{
if ( 32 == info -> mBitrate
|| 48 == info -> mBitrate
|| 56 == info -> mBitrate
|| 80 == info -> mBitrate )
{
if ( info -> mChannelMode != 1 )
return MPEG_AUDIO_ERR;
}
else if ( 224 == info -> mBitrate
|| 256 == info -> mBitrate
|| 320 == info -> mBitrate
|| 384 == info -> mBitrate )
{
if ( 1 == info -> mChannelMode )
return MPEG_AUDIO_ERR;
}
}
// Extension Mode
info -> mExtensionMode = ((hdrBuf[ 3 ] >> 4 ) & 0x03 );
info -> mCopyrightBit = ((hdrBuf[ 3 ] >> 3 ) & 0x01 );
info -> mOriginalBit = ((hdrBuf[ 3 ] >> 2 ) & 0x01 );
// The emphasis indication is here to tell the decoder that the file must be
// de-emphasized, that means the decoder must 're-equalize' the sound after
// a Dolby-like noise suppression. It is rarely used.
info -> mEmphasis = ((hdrBuf[ 3 ]) & 0x03 );
if ( 0x2 == info -> mEmphasis)
return MPEG_AUDIO_ERR;
if (info -> mProtection)
{
// This checksum directly follows the frame header and is a big-endian
// WORD.
// So maybe you shoud convert it to little-endian.
info -> mCRCValue = * ((unsigned short int * )(hdrBuf + 4 ));
bigendian2Local(
(unsigned char * )( & (info -> mCRCValue)), sizeof (info -> mCRCValue) );
}
// 每帧数据的采样数
info -> mSamplesPerFrame = 1152 ;
if ( 1 == info -> mLayer)
{
info -> mSamplesPerFrame = 384 ;
}
else if (info -> mMPEGVersion != 10 && 3 == info -> mLayer)
{
info -> mSamplesPerFrame = 576 ;
}
// 边信息大小
info -> mSideInfoSize = 0 ;
if ( 3 == info -> mLayer)
{
if (info -> mMPEGVersion != 10 ) // MPEG-2/2.5 (LSF)
{
if (info -> mChannelMode != 1 ) // Stereo, Joint Stereo, Dual Channel
info -> mSideInfoSize = 17 ;
else // Mono
info -> mSideInfoSize = 9 ;
}
else // MPEG-1.0
{
if (info -> mChannelMode != 1 ) // Stereo, Joint Stereo, Dual Channel
info -> mSideInfoSize = 32 ;
else // Mono
info -> mSideInfoSize = 17 ;
}
}
info -> mBitrateType = 0 ; // common CBR by default
if (firstFrame)
{
short int reqSize = 4 ;
// DELETE by gansc23 at 2010-12-16 for ignore CRC data size
//if(info->mProtection)
// reqSize += 2 ;
reqSize += info -> mSideInfoSize;
reqSize += 4 ; // "XING" OR "INFO"
// "XING" OR "INFO"
int ret1 = parseVbrXINGHdr(hdrBuf + reqSize - 4 , bufSize - reqSize + 4 , info);
if (MPEG_AUDIO_OK == ret1)
{
goto label_get_XING_or_INFO;
}
else if (MPEG_AUDIO_NEED_MORE == ret1)
{
return MPEG_AUDIO_NEED_MORE;
}
// no "XING" OR "INFO", try to find "VBRI"
reqSize -= ( info -> mSideInfoSize + 4 );
reqSize += 32 ;
ret1 = parseVbrVBRIHdr(hdrBuf + reqSize, bufSize - reqSize, info);
if (MPEG_AUDIO_NEED_MORE == ret1)
{
return MPEG_AUDIO_NEED_MORE;
}
}
label_get_XING_or_INFO:
calculateFrame(info);
return MPEG_AUDIO_OK;
}
// ==============================================================================
// parseVbrXINGHdr
// ==============================================================================
int parseVbrXINGHdr(unsigned char * xingHdr, int bufSize,
MPEGAudioFrameInfo * info)
{
if (bufSize < 4 )
return MPEG_AUDIO_NEED_MORE;
info -> mBitrateType = 0 ;
// for "XING"
if ( (xingHdr[ 0 ] == ' x ' || xingHdr[ 0 ] == ' X ' )
&& (xingHdr[ 1 ] == ' i ' || xingHdr[ 1 ] == ' I ' )
&& (xingHdr[ 2 ] == ' n ' || xingHdr[ 2 ] == ' N ' )
&& (xingHdr[ 3 ] == ' g ' || xingHdr[ 3 ] == ' G ' ) )
{
// VBR(XING)
info -> mBitrateType = 2 ;
}
// for "INFO"
else if ( (xingHdr[ 0 ] == ' i ' || xingHdr[ 0 ] == ' I ' )
&& (xingHdr[ 1 ] == ' n ' || xingHdr[ 1 ] == ' N ' )
&& (xingHdr[ 2 ] == ' f ' || xingHdr[ 2 ] == ' F ' )
&& (xingHdr[ 3 ] == ' o ' || xingHdr[ 3 ] == ' O ' ) )
{
// CBR(INFO)
info -> mBitrateType = 1 ;
}
if ( ! info -> mBitrateType) // no "XING" or "INFO" header
return MPEG_AUDIO_ERR;
int offset = 8 ;
if (bufSize < offset)
return MPEG_AUDIO_NEED_MORE;
// Modified by gansc23 at 2010-12-16 for fixing XING flag bug.
//unsigned char flags = xingHdr[5];
unsigned char flags = xingHdr[7];
if (flags & 0x01 ) // Frames field is present
{
if (bufSize < offset + 4 )
return MPEG_AUDIO_NEED_MORE;
info -> mTotalFrames = * (( int * )(xingHdr + offset));
bigendian2Local((unsigned char * )( & (info -> mTotalFrames)),
sizeof (info -> mTotalFrames));
offset += 4 ;
}
if (flags & 0x02 ) // Bytes field is present
{
if (bufSize < offset + 4 )
return MPEG_AUDIO_NEED_MORE;
info -> mTotalBytes = * (( int * )(xingHdr + offset));
bigendian2Local((unsigned char * )( & (info -> mTotalBytes)),
sizeof (info -> mTotalBytes));
offset += 4 ;
}
if (flags & 0x04 ) // TOC field is present
{
if (bufSize < offset + 100 )
return MPEG_AUDIO_NEED_MORE;
offset += 100 ;
}
if (flags & 0x08 ) // Quality indicator field is present
{
if (bufSize < offset + 4 )
return MPEG_AUDIO_NEED_MORE;
int quality = * (( int * )(xingHdr + offset));
bigendian2Local((unsigned char * )( & quality),
sizeof (quality) );
info -> mQuality = 100 - quality;
offset += 4 ;
}
return MPEG_AUDIO_OK;
}
// ==============================================================================
// parseVbrVBRIHdr
// ==============================================================================
int parseVbrVBRIHdr(unsigned char * vbriHdr, int bufSize,
MPEGAudioFrameInfo * info)
{
if (bufSize < 4 )
return MPEG_AUDIO_NEED_MORE;
info -> mBitrateType = 0 ;
// for "VBRI"
if ( (vbriHdr[ 0 ] == ' v ' || vbriHdr[ 0 ] == ' V ' )
&& (vbriHdr[ 1 ] == ' b ' || vbriHdr[ 1 ] == ' B ' )
&& (vbriHdr[ 2 ] == ' r ' || vbriHdr[ 2 ] == ' R ' )
&& (vbriHdr[ 3 ] == ' i ' || vbriHdr[ 3 ] == ' I ' ) )
{
// VBR
info -> mBitrateType = 3 ;
}
if ( ! info -> mBitrateType) // no "VBRI" header
return MPEG_AUDIO_ERR;
if (bufSize < 26 )
return MPEG_AUDIO_NEED_MORE;
unsigned char * offset = (vbriHdr + 4 );
// VBRI version
info -> mVBRIVersionID = * (( short int * )offset);
bigendian2Local((unsigned char * )( & (info -> mVBRIVersionID)),
sizeof (info -> mVBRIVersionID));
offset += 2 ;
// Delay,不清楚作用
(info -> mVBRIDelay)[ 0 ] = * offset;
(info -> mVBRIDelay)[ 1 ] = * (offset + 1 );
offset += 2 ;
// Quality indicator
info -> mQuality = * (( short int * )offset);
bigendian2Local((unsigned char * )( & (info -> mQuality)),
sizeof (info -> mQuality) ); // 不确定是以大端字节序存放的
info -> mQuality = 100 - info -> mQuality; // 不确定
offset += 2 ;
// total bytes
info -> mTotalBytes = * (( int * )offset);
bigendian2Local((unsigned char * )( & (info -> mTotalBytes)),
sizeof (info -> mTotalBytes));
offset += 4 ;
// total frames number
info -> mTotalFrames = * (( int * )offset);
bigendian2Local((unsigned char * )( & (info -> mTotalFrames)),
sizeof (info -> mTotalFrames));
offset += 4 ;
// Number of entries within TOC table
info -> mEntriesNumInTOCTable = * (( short int * )offset);
bigendian2Local((unsigned char * )( & (info -> mEntriesNumInTOCTable)),
sizeof (info -> mEntriesNumInTOCTable));
offset += 2 ;
// Scale factor of TOC table entries
info -> mTOCTableFactor = * (( short int * )offset);
bigendian2Local((unsigned char * )( & (info -> mTOCTableFactor)),
sizeof (info -> mTOCTableFactor));
offset += 2 ;
// Size per table entry in bytes (max 4)
short int entrySize = * (( short int * )offset);
bigendian2Local((unsigned char * )( & entrySize), sizeof (entrySize));
info -> mEntrySize = entrySize;
offset += 2 ;
// Frames per table entry
info -> mFramesNumPerTable = * (( short int * )offset);
bigendian2Local((unsigned char * )( & (info -> mFramesNumPerTable)),
sizeof (info -> mFramesNumPerTable));
offset += 2 ;
return MPEG_AUDIO_OK;
}
// ==============================================================================
// calculateFrame
// ==============================================================================
int calculateFrame(MPEGAudioFrameInfo * info)
{
info -> mFrameSize = (info -> mSamplesPerFrame * info -> mBitrate * 1000 )
/ ( 8 * info -> mSamplerate)
+ info -> mPaddingSize;
return MPEG_AUDIO_OK;
}
// ==============================================================================
// bigendian2Local
// ==============================================================================
int bigendian2Local(unsigned char * valueAddr, int len)
{
if (len <= 0 || len % 2 != 0 )
return - 1 ;
//#if !defined(BIG_ENDIAN)
if( isBigendian() )
exchangeByteEndian(valueAddr, len);
//#endif
return 0 ;
}
// ==============================================================================
// litendian2Local
// ==============================================================================
int litendian2Local(unsigned char * valueAddr, int len)
{
if (len <= 0 || len % 2 != 0 )
return - 1 ;
//#ifdefined (BIG_ENDIAN)
if( isBigendian() )
exchangeByteEndian(valueAddr, len);
//#endif
return 0 ;
}
// ==============================================================================
// exchangeByteEndian
// ==============================================================================
void exchangeByteEndian(unsigned char * valueAddr, int len)
{
int n = len >> 1 ;
for ( int i = 0 ; i < n; i ++ )
{
unsigned char v = * (valueAddr + i);
* (valueAddr + i) = * (valueAddr + len - 1 - i);
* (valueAddr + len - 1 - i) = v;
}
}
//==============================================================================
// isBigendian
//==============================================================================
inline int isBigendian()
{
const unsigned short int num16 = 0x12ab;
const unsigned char * const pLowByte = (const unsigned char * )num16;
if( 0x12 == *pLowByte )
return 1;
return 0;
}