前言
由于工作需要,需對MP3進行編解碼處理,研究了幾款開源的MP3編解碼器。相對于FFMPEG來說,這幾款都屬于輕量級的編解碼器,更容易移植。
LAME
源碼下載鏈接:https://sourceforge.net/projects/lame/
支持MP3編解碼。編碼出來的MP3音色純厚、空間寬廣、低音清晰、細節(jié)表現(xiàn)良好,它獨創(chuàng)的心理音響模型技術(shù)保證了CD音頻還原的真實性,配合VBR和ABR參數(shù),音質(zhì)幾乎可以媲美CD音頻,但文件體積卻非常小,是目前主流的編碼器。
MAD
MAD(libmad)是一個開源的高精度MPEG音頻解碼庫,支持MPEG-1標準。libmad提供24-bit的PCM輸出,完全定點計算,非常適合在沒有浮點支持的嵌入式硬件平臺上使用。使用libmad提供的一系列API可以實現(xiàn)MP3文件的解碼。
源碼下載鏈接:https://sourceforge.net/projects/mad/
例程minimad.c是在運行前將整個MP3文件讀入內(nèi)存中進行處理,不適合MP3流未知的場景,需改成邊解碼邊寫入MP3的形式,即每次讀入1K MP3數(shù)據(jù),解碼完成再讀入1K,又不影響播放的連續(xù)性,方便在資源緊張的嵌入式系統(tǒng)中運用。
libmad中的mad_decoder_run()進行解碼時,首先會檢測待解碼緩沖區(qū)中是否存在數(shù)據(jù),有則解碼,沒有則調(diào)用input()函數(shù)進行裝載數(shù)據(jù),并返回MAD_FLOW_CONTINUE表示還存在數(shù)據(jù),解碼完成后調(diào)用output()函數(shù)進行處理,如此循環(huán)…直到input()函數(shù)返回MAD_FLOW_STOP表示該MP3數(shù)據(jù)流已經(jīng)完全加載,output()函數(shù)輸出后,表示該MP3文件已完成全部解碼操作。
input()函數(shù)如下,每次調(diào)用讀入FRAME_SIZE_MP3字節(jié)數(shù)據(jù):
static
enum mad_flow input(void *data,
struct mad_stream *stream)
{
PT_Mp3Info ptMp3Info = (PT_Mp3Info)data;
int ret;
int restLen; // unprocessed data's size
int readLen;
if (!feof(fin)) {
restLen = stream->bufend - stream->next_frame;
memcpy(ptMp3Info->inMp3, ptMp3Info->inMp3+ptMp3Info->inLen-restLen, restLen);
readLen = FRAME_SIZE_MP3 - restLen;
int readn = fread(ptMp3Info->inMp3+restLen, sizeof(char), readLen, fin);
ptMp3Info->inLen = restLen + readn;
mad_stream_buffer(stream, ptMp3Info->inMp3, ptMp3Info->inLen);
ret = MAD_FLOW_CONTINUE;
}
else {
ret = MAD_FLOW_STOP;
}
return ret;
}
完整代碼如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "mad.h"
#define FRAME_SIZE_MP3 (1024)
typedef struct _Mp3Info {
unsigned char inMp3[FRAME_SIZE_MP3];
unsigned int inLen;
}T_Mp3Info, *PT_Mp3Info;
static FILE *fin = NULL;
static FILE *fout = NULL;
static int decode(PT_Mp3Info ptMp3Info);
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("%s <inMp3> <outPcm>\n", argv[0]);
return -1;
}
fin = fopen(argv[1], "r");
fout = fopen(argv[2], "wb+");
T_Mp3Info tMp3Info;
decode(&tMp3Info);
fclose(fin);
fclose(fout);
return 0;
}
/*
* This is the input callback. The purpose of this callback is to (re)fill
* the stream buffer which is to be decoded. In this example, an entire file
* has been mapped into memory, so we just call mad_stream_buffer() with the
* address and length of the mapping. When this callback is called a second
* time, we are finished decoding.
*/
static
enum mad_flow input(void *data,
struct mad_stream *stream)
{
PT_Mp3Info ptMp3Info = (PT_Mp3Info)data;
int ret;
int restLen; // unprocessed data's size
int readLen;
if (!feof(fin)) {
restLen = stream->bufend - stream->next_frame;
memcpy(ptMp3Info->inMp3, ptMp3Info->inMp3+ptMp3Info->inLen-restLen, restLen);
readLen = FRAME_SIZE_MP3 - restLen;
int readn = fread(ptMp3Info->inMp3+restLen, sizeof(char), readLen, fin);
ptMp3Info->inLen = restLen + readn;
mad_stream_buffer(stream, ptMp3Info->inMp3, ptMp3Info->inLen);
ret = MAD_FLOW_CONTINUE;
}
else {
ret = MAD_FLOW_STOP;
}
return ret;
}
/*
* The following utility routine performs simple rounding, clipping, and
* scaling of MAD's high-resolution samples down to 16 bits. It does not
* perform any dithering or noise shaping, which would be recommended to
* obtain any exceptional audio quality. It is therefore not recommended to
* use this routine if high-quality output is desired.
*/
static inline
signed int scale(mad_fixed_t sample)
{
/* round */
sample += (1L << (MAD_F_FRACBITS - 16));
/* clip */
if (sample >= MAD_F_ONE)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
/* quantize */
return sample >> (MAD_F_FRACBITS + 1 - 16);
}
/*
* This is the output callback function. It is called after each frame of
* MPEG audio data has been completely decoded. The purpose of this callback
* is to output (or play) the decoded PCM audio.
*/
static
enum mad_flow output(void *data,
struct mad_header const *header,
struct mad_pcm *pcm)
{
unsigned int nchannels, nsamples;
mad_fixed_t const *left_ch, *right_ch;
/* pcm->samplerate contains the sampling frequency */
nchannels = pcm->channels;
nsamples = pcm->length;
left_ch = pcm->samples[0];
right_ch = pcm->samples[1];
while (nsamples--) {
signed int sample;
/* output sample(s) in 16-bit signed little-endian PCM */
sample = scale(*left_ch++);
char high = (sample >> 0) & 0xff;
char low = (sample >> 8) & 0xff;
// putchar((sample >> 0) & 0xff);
// putchar((sample >> 8) & 0xff);
fwrite(&high, sizeof(char), 1, fout);
fwrite(&low, sizeof(char), 1, fout);
if (nchannels == 2) {
sample = scale(*right_ch++);
// putchar((sample >> 0) & 0xff);
// putchar((sample >> 8) & 0xff);
high = (sample >> 0) & 0xff;
low = (sample >> 8) & 0xff;
fwrite(&high, sizeof(char), 1, fout);
fwrite(&low, sizeof(char), 1, fout);
}
}
return MAD_FLOW_CONTINUE;
}
/*
* This is the error callback function. It is called whenever a decoding
* error occurs. The error is indicated by stream->error; the list of
* possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
* header file.
*/
static
enum mad_flow error(void *data,
struct mad_stream *stream,
struct mad_frame *frame)
{
PT_Mp3Info ptMp3Info = (PT_Mp3Info)data;
fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %lu\n",
stream->error, mad_stream_errorstr(stream),
stream->this_frame - ptMp3Info->inMp3);
/* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
return MAD_FLOW_CONTINUE;
}
/*
* This is the function called by main() above to perform all the decoding.
* It instantiates a decoder object and configures it with the input,
* output, and error callback functions above. A single call to
* mad_decoder_run() continues until a callback function returns
* MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
* signal an error).
*/
static
int decode(PT_Mp3Info ptMp3Info)
{
struct mad_decoder decoder;
int result;
if (ptMp3Info == NULL) {
printf("ptMp3Info is NULL\n");
return -1;
}
/* configure input, output, and error functions */
mad_decoder_init(&decoder, ptMp3Info,
input, 0 /* header */, 0 /* filter */, output,
error, 0 /* message */);
/* start decoding */
result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
/* release the decoder */
mad_decoder_finish(&decoder);
return result;
}
tinymp3
支持MP3編解碼,代碼量少,適合在單片機上移植。
源碼下載鏈接:https://github.com/cpuimage/tinymp3
minimp3
僅支持MP3解碼,只有一個頭文件,適合在單片機上移植。
源碼下載鏈接:https://github.com/lieff/minimp3
minimp3的使用只需調(diào)用一個函數(shù)即可實現(xiàn)解碼文章來源:http://www.zghlxwxcb.cn/news/detail-407180.html
int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info);
消耗的 MP3 數(shù)據(jù)的大小在定義的mp3dec_frame_info_t結(jié)構(gòu)中的frame_bytes字段中返回,必須在下一次解碼器調(diào)用之前從輸入緩沖區(qū)中刪除對應(yīng)于 frame_bytes 字段的數(shù)據(jù)。
解碼函數(shù)返回已解碼樣本的數(shù)量samples??赡艹霈F(xiàn)以下情況:
0: 在輸入緩沖區(qū)中未找到 MP3 數(shù)據(jù)
384: Layer 1
576: MPEG 2 Layer 3
1152: Otherwise
samples 和 frame_bytes 字段值:
samples > 0 和 frame_bytes > 0: 成功解碼
samples == 0 和 frame_bytes > 0: 解碼器跳過了 ID3 或無效數(shù)據(jù)
samples == 0 和 frame_bytes == 0: 數(shù)據(jù)不足
參考代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-407180.html
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#define MINIMP3_IMPLEMENTATION
#include "minimp3.h"
int main(int argc, char *argv[])
{
unsigned char *inMp3 = NULL;
int totalLen = 0;
if (argc != 3) {
printf("%s <inMp3> <outPcm>\n", argv[0]);
return -1;
}
//打開MP3文件
FILE* fin = fopen(argv[1], "r");
//獲取MP3文件長度
fseek(fin, 0, SEEK_END);
totalLen = (int)ftell(fin);
//讀取整個MP3文件
fseek(fin, 0, SEEK_SET);
inMp3 = malloc(totalLen);
fread(inMp3, 1, totalLen, fin);
fclose(fin);
//定義mp3dec_frame_info_t
mp3dec_frame_info_t info;
short outPcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
int inLen = 0;
//逐幀解碼
int samples = mp3dec_decode_frame(&mp3d, inMp3, totalLen, outPcm, &info);
while(samples) {
fwrite(outPcm, sizeof(short), samples, fout);
inLen += info.frame_bytes;
samples = mp3dec_decode_frame(&mp3d, inMp3 + inLen, totalLen - inLen, outPcm, &info);
}
free(inMp3);
inMp3 = NULL;
fclose(fout);
return 0;
}
到了這里,關(guān)于常用的開源MP3編解碼器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!