解决了lame-3.100交叉编译问题后,照着API文档实现了mp3编码,解码部分lame也有实现,使用的是mpg123的老版本库,通过hip这个接口实现,在lame.h中就hip_decode_init , hip_decode_exit, hip_decode , hip_decode_headers 这么几个函数体现。
通过下面这个循环解析出帧格式,发现有很多mp3解析不对,兼容性不好。
while (!mp3data.header_parsed) {
len = fread(buf, 1, sizeof(buf), inputFile);
if (len != sizeof(buf))
return -1;
ret = hip_decode1_headers(hip, buf, len, pcm_l, pcm_r, &mp3data);
if (-1 == ret)
{
break;
}
}
但是,在shell中使用指令lame --decode可以把mp3转换为wav格式,解析不对的mp3都使用正常,发现lame decode并不是使用上面的调用方法。把lame库代码添加到source insight工程,然后查找main函数,跳转到lame_main,发现指令解析mp3时主要调用的get_audio.h中的 int init_infile(lame_t gfp, char const * inPath); 函数。然后顺藤摸瓜,找到了函数int
lame_decode_initfile(FILE * fd, mp3data_struct * mp3data, int *enc_delay, int *enc_padding)
显然,要从库里调用这个函数不太现实,把frontend里的文件移植到工程中也比较麻烦,好在我只需要播放mp3,不太关注帧格式的信息,就从lame_decode_initfile提取部分代码到自己的函数中。
hip_decode_init();
memset(mp3data, 0, sizeof(mp3data_struct));
len = 4;
if (fread(buf, 1, len, fd) != len)
return -1; /* failed */
while (buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3') {
len = 6;
if (fread(&buf[4], 1, len, fd) != len)
return -1; /* failed */
len = lenOfId3v2Tag(&buf[6]);
if (global.in_id3v2_size < 1) {
global.in_id3v2_size = 10 + len;
global.in_id3v2_tag = malloc(global.in_id3v2_size);
if (global.in_id3v2_tag) {
memcpy(global.in_id3v2_tag, buf, 10);
if (fread(&global.in_id3v2_tag[10], 1, len, fd) != len)
return -1; /* failed */
len = 0; /* copied, nothing to skip */
}
else {
global.in_id3v2_size = 0;
}
}
fseek(fd, (long) len, SEEK_CUR);
len = 4;
if (fread(&buf, 1, len, fd) != len)
return -1; /* failed */
}
aid_header = check_aid(buf);
if (aid_header) {
if (fread(&buf, 1, 2, fd) != 2)
return -1; /* failed */
aid_header = (unsigned char) buf[0] + 256 * (unsigned char) buf[1];
if (global_ui_config.silent < 9) {
console_printf("Album ID found. length=%i \n", aid_header);
}
/* skip rest of AID, except for 6 bytes we have already read */
fseek(fd, aid_header - 6, SEEK_CUR);
/* read 4 more bytes to set up buffer for MP3 header check */
if (fread(&buf, 1, len, fd) != len)
return -1; /* failed */
}
len = 4;
while (!is_syncword_mp123(buf)) {
unsigned int i;
for (i = 0; i < len - 1; i++)
buf[i] = buf[i + 1];
if (fread(buf + len - 1, 1, 1, fd) != 1)
return -1; /* failed */
}
if ((buf[2] & 0xf0) == 0) {
freeformat = 1;
}
/* now parse the current buffer looking for MP3 headers. */
/* (as of 11/00: mpglib modified so that for the first frame where */
/* headers are parsed, no data will be decoded. */
/* However, for freeformat, we need to decode an entire frame, */
/* so mp3data->bitrate will be 0 until we have decoded the first */
/* frame. Cannot decode first frame here because we are not */
/* yet prepared to handle the output. */
ret = hip_decode1_headersB(global.hip, buf, len, pcm_l, pcm_r, mp3data, enc_delay, enc_padding);
if (-1 == ret)
return -1;
/* repeat until we decode a valid mp3 header. */
while (!mp3data->header_parsed) {
len = fread(buf, 1, sizeof(buf), fd);
if (len != sizeof(buf))
return -1;
ret =
hip_decode1_headersB(global.hip, buf, len, pcm_l, pcm_r, mp3data, enc_delay,
enc_padding);
if (-1 == ret)
return -1;
}
if (mp3data->bitrate == 0 && !freeformat) {
return lame_decode_initfile(fd, mp3data, enc_delay, enc_padding);
}
if (mp3data->totalframes > 0) {
/* mpglib found a Xing VBR header and computed nsamp & totalframes */
}
else {
/* set as unknown. Later, we will take a guess based on file size
* ant bitrate */
mp3data->nsamp = MAX_U_32_NUM;
}
再把调用的几个小函数也copy过来,fskip直接用fseek替换了, is_syncword_mp123中不是sf_mp3就直接return 0。
大致解读下这段函数意思,就是根据mp3的格式依次读取内容,首先识别ID3V2内容,然后是唱片集信息,然后是VBR帧头,注意vbr帧头的前4字节和mp3帧头的4字节格式相同,但总处在layer I层,就是这点害了我最上边那段解析的代码,读出来是不对的采样率和比特率,vbr帧头4字节后面是“Xing”火灾“lame”, 跳过vbr帧头才是mp3音频帧.
解码的时候计算一下帧长度
// = 每帧采样率 / 8 × 比特率 / 采样频率 + 填充
framesize = mp3data.framesize / 8 * mp3data.bitrate * 1000 / mp3data.samplerate + 1;
vbr的时候帧长可变,好在hip会记录缓冲区上一次解码的地址,就默认每次都喂 framesize 个字节就能播放了,
使用的是hip_decode1,只解一帧或者不够一帧就返回0。
MP3帧格式可搜索一下,有很多文章讲的很详细了。