libmad音頻解碼庫分析 - libmad簡介
“mad.h”頭文件定義了libmad的數據結構及API函數[15]。
表4 libmad中的主要數據結構
主要數據結構 |
作用 |
struct mad_stream |
存放解碼前的Bitstream數據 |
struct mad_synth |
存放解碼合成濾波后的PCM數據 |
struct mad_pcm |
定義了音頻的采樣率,聲道個數和PCM采樣數據,用來初始化音頻 |
struct mad_frame |
記錄MPEG幀解碼后PCM數據的數據結構,其中的mad_header用來記錄MPEG幀的基本信息,比如MPEG層數、聲道模式、流比特率、采樣比特率。聲道模式包括單聲道、雙聲道、聯合立體混音道以及一般立體聲。 |
MAD通過回調函數機制來實現解碼,每個回調函數會返回一個枚舉類型mad_flow,通過mad_flow可以控制解碼的過程。在未經處理的情況下,MAD一般輸出32bit,以little endian格式存放在mad_fixed_t中的數據。但是大多數的聲卡并不能支持輸出高達32bit精度的數據,因而還必須對mad_fixed_t進行量化,圓滑處理以及抖動,使到采樣信號降到16bit精度。MAD負責的只是解碼的過程,它工作過程是:從外部獲取輸入,逐幀解碼,在解碼的過程中返回信息,然后得到解碼結果。開發人員要手動設置輸入輸出。
編程實現解碼的方法為:初始化mad_decoder,里面包含了指向輸入、輸出、濾波、錯誤和消息回調函數的指針。通過mad_decoder_init()實現初始化[16]。
struct mad_decoder decoder;
struct my_playbuf playbuf; //設置數據緩沖區
mad_decoder_init(&decoder,&playbuf,input_func,header_func,/*filter*/0, output_func, /*error*/ 0, /* message */ 0);
在這個初始化函數里面,回調輸入函數指向了input_func,處理幀頭信息的函數指向了header_func,而輸出函數則為output_func。其它的濾波,錯誤和信息函數沒有設置,置0。
接著,MAD進入了一個解碼的循環過程:
當解碼函數里面的數據解碼完畢時,調用input_func函數;
當input_func函數告知解碼函數全部數據已經解碼完畢,則MAD處理退出;
對幀頭進行解碼,調用header_func函數;
對幀中的主數據進行解碼;
調用filter_func函數;
將解碼數據輸出,調用output_func函數;
重復上述步驟。
MAD在每進行一幀的解碼結束后都會詢問mad_flow的狀態,以決定是否進行下一幀的解碼。enum mad_flow的數據結構定義如下:
enum mad_flow{
MAD_FLOW_CONTINUE = 0x0000, /*繼續進行下一幀的解碼*/
MAD_FLOW_STOP = 0x0010, /*停止對該比特流的解碼并正常退出*/
MAD_FLOW_BREAK = 0x0010, /*停止對該比特流的解碼并返回錯誤*/
MAD_FLOW_IGNORE = 0x0020 /*不解碼該幀,跳入下一幀*/
};
大多數情況下回調函數會返回MAD_FLOW_CONTINUE。要自定義實現的回調函數的聲明格式為:
enum mad_flow (*input_func)(void *, struct mad_stream *);
enum mad_flow (*header_func)(void *, struct mad_header const *);
enum mad_flow (*filter_func)(void *, struct mad_stream const *, struct mad_frame *);
enum mad_flow (*output_func)(void *, struct mad_header const *, struct mad_pcm *);
enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
enum mad_flow (*message_func)(void *, void *, unsigned int *);
其中void *指針將緩沖數據傳遞給這些回調函數,由回調函數對數據進行處理。Input_func函數一般會執行以下操作:
if (more_data_available)
buffer = refill_buffer();
mad_stream_buffer(stream, buffer, length_of_buffer);
return MAD_FLOW_CONTINUE;
else return MAD_FLOW_STOP;
header_func函數會根據mad_header指向的幀頭從中讀取重要的幀信息,如將讀取到的幀長度賦值給mad_timer_t,可以從mad.h中得知存放這些信息的數據結構。
在output_func函數中,利用指向PCM數據的指針mad_pcm,執行類似以下操作:
mad_fixed_t *left_ch = pcm->samples[0], *right_ch =pcm->samples[1]; //將采樣數據分別輸出到左右聲道
int nsamples = pcm->length;
signed int sample;
unsigned char * buffer = some_buffer;
unsigned char * ptr = buffer;
while (nsamples--)
{
sample = (signed int) do_downsample(*left_ch++)
*ptr++ = (unsigned char) (sample >> 0);
*ptr++ = (unsigned char) (sample >> 8);
sample = (signed int) do_downsample(*right_ch++)
*ptr++ = (unsigned char) (sample >> 0);
*ptr++ = (unsigned char) (sample >> 8);
//處理左右聲道采樣數據,輸出16bit little endian格式PCM
}
定義好各回調函數之后,便可以開始解碼:
mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
解碼完畢后,調用mad_decoder_finish(&decoder);
MAD (libmad)是一個開源的高精度 MPEG 音頻解碼庫,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 輸出,完全是定點計算,非常適合沒有浮點支持的平臺上使用。使用 libmad 提供的一系列 API,就可以非常簡單地實現 MP3 數據解碼工作。在 libmad 的源代碼文件目錄下的 mad.h 文件中,可以看到絕大部分該庫的數據結構和 API 等。
本文用到的 libmad 中的主要數據結構有:struct mad_stream, struct mad_synth, struct mad_frame
。它們的定義如下:
struct mad_stream {
unsigned char const *buffer; /* input bitstream buffer */
unsigned char const *bufend; /* end of buffer */
unsigned long skiplen; /* bytes to skip before next frame */
int sync; /* stream sync found */
unsigned long freerate; /* free bitrate (fixed) */
unsigned char const *this_frame; /* start of current frame */
unsigned char const *next_frame; /* start of next frame */
struct mad_bitptr ptr; /* current processing bit pointer */
struct mad_bitptr anc_ptr; /* ancillary bits pointer */
unsigned int anc_bitlen; /* number of ancillary bits */
unsigned char (*main_data)[MAD_BUFFER_MDLEN];
/* Layer III main_data() */
unsigned int md_len; /* bytes in main_data */
int options; /* decoding options (see below) */
enum mad_error error; /* error code (see above) */
}; |
如果緩沖區最后一個 MPEG 數據幀只有部分數據包括在緩沖區中,那么 struct mad_stream
中的 next_frame
域指到不完整數據的開始地址。由于緩沖區的 MPEG 數據幀不一定完整,所以不完整的 MPEG 幀的數據必須拷貝到下一次解碼操作的緩沖區中,進行再次解碼。這里我們還看到 bufend
指向緩沖區數據的最后地址,也就是最后一字節的地址加 1 的位置。mad_stream.bufend – mad_stream.next_frame
就是剩余的未被解碼的 MPEG 幀的數據的字節數量(假設此幀在緩沖區中不完整)。mad_stream
的 error
域用來記錄操作 mad_stream
得到的錯誤代碼。錯誤代碼在 mad.h 中有很詳細的定義。
struct mad_synth {
mad_fixed_t filter[2][2][2][16][8]; /* polyphase filterbank outputs */
/* [ch][eo][peo][s][v] */
unsigned int phase; /* current processing phase */
struct mad_pcm pcm; /* PCM output */
}; |
mad_synth
中的關鍵域 pcm
保存解碼和合成后得到的 PCM 數據。
struct mad_pcm {
unsigned int samplerate; /* sampling frequency (Hz) */
unsigned short channels; /* number of channels */
unsigned short length; /* number of samples per channel */
mad_fixed_t samples[2][1152]; /* PCM output samples [ch][sample] */
}; |
struct mad_pcm
定義了音頻的采樣率、每個聲道個數以及最后的 PCM 采樣數據。這些參數可用來初始化音頻設備。
struct mad_frame {
struct mad_header header; /* MPEG audio header */
int options; /* decoding options (from stream) */
mad_fixed_t sbsample[2][36][32]; /* synthesis subband filter samples */
mad_fixed_t (*overlap)[2][32][18]; /* Layer III block overlap data */
}; |
mad_frame
是記錄 MPEG 幀解碼后的數據的數據結構,其中的 mad_header
尤其重要,其用來記錄 MPEG 幀的一些基本信息,比如 MPEG 層數、聲道模式、流比特率、采樣比特率等等。聲道模式包括單聲道、雙聲道、聯合立體混音聲以及一般立體聲。
enum mad_mode {
MAD_MODE_SINGLE_CHANNEL = 0, /* single channel */
MAD_MODE_DUAL_CHANNEL = 1, /* dual channel */
MAD_MODE_JOINT_STEREO = 2, /* joint (MS/intensity) stereo */
MAD_MODE_STEREO = 3 /* normal LR stereo */
};
struct mad_header {
enum mad_layer layer; /* audio layer (1, 2, or 3) */
enum mad_mode mode; /* channel mode */
int mode_extension; /* additional mode info */
enum mad_emphasis emphasis; /* de-emphasis to use */
unsigned long bitrate; /* stream bitrate (bps) */
unsigned int samplerate; /* sampling frequency (Hz) */
unsigned short crc_check; /* frame CRC accumulator */
unsigned short crc_target; /* final target CRC checksum */
int flags; /* flags */
int private_bits; /* private bits */
mad_timer_t duration; /* audio playing time of frame */
}; |
下面就本文使用的 API 的功能做簡單介紹。
在本文中用到的 API 包括:
void mad_stream_init(struct mad_stream *)
void mad_synth_init(struct mad_synth *);
void mad_frame_init(struct mad_frame *); |
以上3個 API 初始化解碼需要的數據結構。
void mad_stream_buffer(struct mad_stream *, unsigned char const *, unsigned long); |
此函數把原始的未解碼的 MPEG 數據和 mad_stream
數據結構關聯,以便使用 mad_frame_decode( )
來解碼 MPEG 幀數據。
int mad_frame_decode(struct mad_frame *, struct mad_stream *); |
把 mad_stream
中的 MPEG 幀數據解碼。
void mad_synth_frame(struct mad_synth *, struct mad_frame const *); |
把解碼后的音頻數據合成 PCM 采樣。
void mad_stream_finish(struct mad_stream *);
void mad_frame_finish(struct mad_frame *);
mad_synth_finish(struct mad_synth); |
以上 3 個 API 在解碼完畢后使用,釋放 libmad 占用的資源等。