mp3帧头分析

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
0x00000002 - Bytes field is present
0x00000004 - TOC field is present 
0x00000008 - Quality indicator field is present

0x0007
(means Frames, Bytes & TOC valid)

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
from 0 - best quality to 100 - worst quality (optional)

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

附上我自己写的源码,抛砖引玉,欢迎大家友好地讨论 (gansc23@gmail.com):

 

 

复制代码
 
 
/*
* MPEGAudioFrame.h
*
* Created on: 2010-11-26
* Author: gansc23@gmail.com
*
* 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: gansc23@gmail.com
*
* 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 ;

//#if defined (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;

}

转自 https://www.cnblogs.com/gansc23/archive/2010/11/27/1889537.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值