使用lame解碼mp3

通過這幾天查資料,翻代碼。這裏總結一下lame解析MP3的過程以及遇到問題。c++

開發環境是Linux下c++開發shell

使用lame解析MP3,主要包含如下幾個頭文件。緩存

#include <lame.h>
#include <timestatus.h>
#include <set_get.h>
#include <get_audio.h>
#include <lame_reader_def.h>

目前網上查到有關lame 解析MP3的資料很少。大部分是使用lame壓縮MP3格式。大多人用libmad解碼MP3。函數

惟一查到的幾個有關lame解析MP3的demo格式雷同,兼容性不好。在測試過程當中遇到過不兼容的狀況。測試

這種方式我也把方法列出來。主要使用mpglib_interfac.c文件中的hip_decode1_headers(……),以及hip_decode(......)方法。spa

點進去看一下就知道其實他們都是對hip_decode1_headerB()方法的封裝,只不過前者須要提供mp3data_struct結構體指針。方便將mp3的數據格式讀取出來,作一些前期工做。後者只是直接就開始解析了。命令行

 

源代碼在公司內網中,取不出來,因此如下代碼都是手敲複製出來的,可能會拼寫錯。別期望照搬過去就能直接運行,看懂先。3d

#include <lame.h>
#include <string.h>
//定義數據
FILE* m_filename;
hip_t m_hip;
lame_t m_lame_gfp;
mp3data_struct m_mp3data; //存儲MP3的各種數據,採樣率,通道數,幀數等

short int m_pcm_1[9000];
short int m_pcm_r[9000];

unsigned char m_mp3_buffer[9000]; //mp3文件中讀取解析以前的數據緩存
int sampleRate; //採樣率
int channelCount; //通道數

//初始化階段
m_lame_gfp = lame_init();
lame_set_decode_only(m_lame_gfp,1);
m_hip = hip_decode_init();

m_filename = fopen("voice.MP3","rb");

/**
這一段的邏輯就是,循環從文件中讀取16個字節出來,而後交給hip_decode1_header1函數解析。
m_hip中會保存上一次解析的位置,因此直接不斷的丟一些數據給hip_decode1_header1函數就行。
當成功解析出一個幀頭的數據時,實際上就獲得了整個MP3的採樣率和通道數了。大部分MP3每一幀的信息是同樣的。固然也不是所有的。
因此說對大對數MP3是兼容的。這裏成功解析出來以後,m_mp3data.header_parsed值會變成1,所以可退出這個過程。
至於每次多輸入一些數據也是能夠的,例如將16設置成210,418均可以。
*/
int reslen = -1;
do{
    reslen = fread(m_mp3_buff,,sizeof(char),16,m_filename);
    hip_decode1_header1(m_hip,m_mp3_buffer,reslen,m_pcm_l,m_pcm_r,&m_mp3data);
    sampleRate = m_mp3data.samplerate;
    channelCount = m_mp3data.stereo;
}while(!m_mp3data.header_parsed && relen>0);

fseek(m_filename,0,SEEK_SET); //將文件指針撥回起點。


//循環解析MP3音頻數據階段
int iread;
unsigned char data[8192]; //存放解析以後的pcm

static FILE *output_file = fopen(output.pcm,"W+");

/**
這裏邏輯其實和上面的解析幀頭是同樣的。循環向hip_decode函數中投喂MP3數據,通過解析以後的pcm數據放在m_pcm_l以及m_pcm_r中。
hip_decode()返回值iread是採樣數,不是字符長度,這點要注意。通常一個採樣點是16位長,2個字節大小,與short int的長度一致。

*/
 while(reslen = fread(m_mp3buff,sizeof(char),418,m_filename)){
 	if(reslen > 0){
        iread = hip_decode(m_hip,m_mp3_buffer,reslen,m_pcm_l,m_pcm_r);
        if(iread>0){
        	unsigned j = 0;
            for(int i = 0;i<sample;i++){ //雙通道的話就將採樣點交叉合併在一塊兒。
            	memcpy(data + j,m_pcm_l + i , 2);
                j+=2;
                if(m_mp3data.stereo == 2){
                    memcpy(data + j,m_pcm_r + i , 2);
                    j+=2;
                }
            }
        }
    }
     
     // 這裏就能夠將data中的數據寫入文件中,或者輸入至播放設備中。
     fwrite(data,sizeof(short)*m_mp3dtat.stereo,sample,output_file);
     
 }

if(m_hip){
    hip_decode_exit(m_hip);
}

if(m_filename){
	fclose(m_filename);
}
if(output_file){
	fclose(output_file);
}
以上代碼是我製做解析MP3的程序的初版本,能暴力解析大部分MP3.直到有一天一個大佬問我這個解析程序作的怎麼樣,他那邊要參考一下個人解析過程,而後甩給我一個12m的MP3,說,試試解析這個MP3。這MP3能在各個播放器上正常播放。
很順利的解析出來的pcm只有400k大小。顯然是失敗了,通常44100的Mp3解析出來的pcm是原來文件的2-4倍大小。解析過程一直報這個問題:(n行)。
htp: bitstream problem, resyncing skipping xxxx byte...   
網上找了一圈也沒找到緣由,沒辦法只有啃源代碼,再加一點點的揣測。
大概的意思就是找到下一幀幀頭,以前跳過了多少個字符數據。若是說MP3跳過一兩次,百來個字符,那都算正常。
可是大佬給個人MP3解析的時候幾乎跳過了99%的字符。顯然是解析失敗了。
這個地方卡了我一成天。剖析源代碼。分析MP3格式。
 
 
在網上查了不少資料,獲得一個結論。
在shell中直接經過命令lame -decode 將MP3轉WAV時是能正常將上述的有問題的MP3轉化的。由於,命令行的入口函數調用的方法就不是hip_decode_init()  ->  hip_decode_header()  ->  hip_decode()  -> hip_decode_exit()這個過程。
而是採用另外一套流程。這裏我就直接將相關的代碼列出來:
#include <lame.h>
#include <timestatus.h>
#include <set_get.h>
#include <get_audio.h>
#include <lame_reader_def.h>
//定義變量
lame_t m_lame_gfp;
DecoderProgress m_dp;
int sampleRate; //採樣率
int channelCount; //通道數

///初始化階段
int lame_err = 0;
m_lame_gfp = lame_init();
if(m_lame_gfp == NULL){
	//LOGE("初始化失敗。");
    return -1;
}
lame_set_write_id3tag_automatic(m_lame_gfp,0); 
lame_set_out_samplerate(m_lame_gfp,441000); //設置輸出的pcm 的採樣率
lame_set_decode_only(m_lame_gfp,1);

lame_err = init_infile(m_lame_gfp,"voice.mp3"); //初始化讀取文件
if(lame_err < 0){
	//LOGE("初始化失敗。");
    return -1;
}

lame_err = lame_init_params(m_lame_gfp);
if(lame_err < 0){
	//LOGE("fail");
    return -1;
}

/**
set_get.c中包含了各種獲取MP3音頻信息的函數,直接調用便可。下面就是獲取採樣率和通道數的函數。
能夠查看源代碼中還有獲取其餘信息的函數。
*/
sampleRate = lame_get_out_samplerate(m_lame_gfp);
channelCount = lame_get_num_channels(m_lame_gfp);


//解析過程
static FILE *output_file = fopen(output.pcm,"W+");
short int pcm_buffer[2][1152];
int iread = 0;
unsigned char data[8192]; //存放解析以後的pcm

/**
這裏通常是以幀爲單位,一次循環輸出一幀數據。
*/
do{
    iread = get_audio16(m_lame_gfp,pcm_buff);
    if(m_dp != NULL){
    	decoder_progress(m_dp,&global_decode.mp3input,iread);
    }
    
    if(lame_get_num_channels(m_lame_gfp) !=2 ){
        memcpy(data,pcm_buffer[0],iread);
    }else{
        unsigned int j = 0;
        for(int i = 0;i < iread ; i++ , j += 4){
        	memcpy(data + j , pcm_buffer[0] + i , 2);
            memcpy(data + j + 2, pcm_buffer[1] + i , 2);
        };
    }

    // 這裏就能夠將data中的數據寫入文件中,或者輸入至播放設備中。
     fwrite(data,sizeof(short)*m_mp3dtat.stereo,sample,output_file);
    
}while(iread > 0);

close_infile(); //關閉文件
if(output_file){
	fclose(output_file);
}
if(m_dp){
    decoder_progress_finish(m_dp);
    m_dp = NULL;
}
if(m_lame_gfp){
    lame_close(m_lame_gfp);
    m_lame_gfp = NULL;
}
相關文章
相關標籤/搜索