使用lame解码mp3
经过这几天查资料,翻代码。这里总结一下lame解析MP3的过程以及遇到问题。开发环境是Linux下使用eclipse编译器。使用lame解析MP3,主要包含以下几个头文件。#include <lame.h>#include <timestatus.h>#include <set_get.h>#include <get_audio.h>#include
·
经过这几天查资料,翻代码。这里总结一下lame解析MP3的过程以及遇到问题。
开发环境是Linux下c++开发
使用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(......)方法。
点进去看一下就知道其实他们都是对hip_decode1_headerB()方法的封装,只不过前者需要提供mp3data_struct结构体指针。方便将mp3的数据格式读取出来,做一些前期工作。后者只是直接就开始解析了。
源代码在公司内网中,取不出来,所以以下代码都是手敲复制出来的,可能会拼写错。别指望照搬过去就能直接运行,看懂先。
#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;
}
更多推荐
已为社区贡献1条内容
所有评论(0)