MP3幀頭格式(CBR,VBR), 附源碼

轉載:http://www.cnblogs.com/gansc23/archive/2010/11/27/1889537.html


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

附上我自己寫的源碼,拋磚引玉,歡迎大家友好地討論 ([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;
    
//#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;
}


發佈了11 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章