常用的开源MP3编解码器

这篇具有很好参考价值的文章主要介绍了常用的开源MP3编解码器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

由于工作需要,需对MP3进行编解码处理,研究了几款开源的MP3编解码器。相对于FFMPEG来说,这几款都属于轻量级的编解码器,更容易移植。

LAME

源码下载链接:https://sourceforge.net/projects/lame/
支持MP3编解码。编码出来的MP3音色纯厚、空间宽广、低音清晰、细节表现良好,它独创的心理音响模型技术保证了CD音频还原的真实性,配合VBR和ABR参数,音质几乎可以媲美CD音频,但文件体积却非常小,是目前主流的编码器。

MAD

MAD(libmad)是一个开源的高精度MPEG音频解码库,支持MPEG-1标准。libmad提供24-bit的PCM输出,完全定点计算,非常适合在没有浮点支持的嵌入式硬件平台上使用。使用libmad提供的一系列API可以实现MP3文件的解码。
源码下载链接:https://sourceforge.net/projects/mad/
例程minimad.c是在运行前将整个MP3文件读入内存中进行处理,不适合MP3流未知的场景,需改成边解码边写入MP3的形式,即每次读入1K MP3数据,解码完成再读入1K,又不影响播放的连续性,方便在资源紧张的嵌入式系统中运用。
libmad中的mad_decoder_run()进行解码时,首先会检测待解码缓冲区中是否存在数据,有则解码,没有则调用input()函数进行装载数据,并返回MAD_FLOW_CONTINUE表示还存在数据,解码完成后调用output()函数进行处理,如此循环…直到input()函数返回MAD_FLOW_STOP表示该MP3数据流已经完全加载,output()函数输出后,表示该MP3文件已完成全部解码操作。
input()函数如下,每次调用读入FRAME_SIZE_MP3字节数据:

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的使用只需调用一个函数即可实现解码

int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_sample_t *pcm, mp3dec_frame_info_t *info);

消耗的 MP3 数据的大小在定义的mp3dec_frame_info_t结构中的frame_bytes字段中返回,必须在下一次解码器调用之前从输入缓冲区中删除对应于 frame_bytes 字段的数据。
解码函数返回已解码样本的数量samples。可能出现以下情况:
0: 在输入缓冲区中未找到 MP3 数据
384: Layer 1
576: MPEG 2 Layer 3
1152: Otherwise

samples 和 frame_bytes 字段值:
samples > 0 和 frame_bytes > 0: 成功解码
samples == 0 和 frame_bytes > 0: 解码器跳过了 ID3 或无效数据
samples == 0 和 frame_bytes == 0: 数据不足

参考代码如下:文章来源地址https://www.toymoban.com/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;
}

到了这里,关于常用的开源MP3编解码器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 编码器 | 基于 Transformers 的编码器-解码器模型

    基于 transformer 的编码器-解码器模型是 表征学习 和 模型架构 这两个领域多年研究成果的结晶。本文简要介绍了神经编码器-解码器模型的历史,更多背景知识,建议读者阅读由 Sebastion Ruder 撰写的这篇精彩 博文。此外,建议读者对 自注意力 (self-attention) 架构 有一个基本了解

    2024年02月08日
    浏览(61)
  • 【Transformer系列(1)】encoder(编码器)和decoder(解码器)

    前言 这个专栏我们开始学习transformer,自推出以来transformer在深度学习中占有重要地位,不仅在NLP领域,在CV领域中也被广泛应用,尤其是2021年,transformer在CV领域可谓大杀四方。 在论文的学习之前,我们先来介绍一些专业术语。本篇就让我们先来认识一下encoder和decoder吧!

    2024年03月25日
    浏览(61)
  • 【Netty】Netty 解码器(十二)

    回顾Netty系列文章: Netty 概述(一) Netty 架构设计(二) Netty Channel 概述(三) Netty ChannelHandler(四) ChannelPipeline源码分析(五) 字节缓冲区 ByteBuf (六)(上) 字节缓冲区 ByteBuf(七)(下) Netty 如何实现零拷贝(八) Netty 程序引导类(九) Reactor 模型(十) 工作原理

    2024年02月07日
    浏览(54)
  • 22.Netty源码之解码器

    https://mp.weixin.qq.com/s/526p5f9fgtZu7yYq5j7LiQ Netty 常用解码器类型: ByteToMessageDecoder/ReplayingDecoder 将字节流解码为消息对象; MessageToMessageDecoder 将一种消息类型解码为另外一种消息类型。 自定义一次解码器ByteToMessageDecoder解码器,如果读到的字节大小为4,那么认为读取到了1个完整的数

    2024年02月14日
    浏览(46)
  • 23.Netty源码之内置解码器

    在前两节课我们介绍了 TCP 拆包/粘包的问题,以及如何使用 Netty 实现自定义协议的编解码。可以看到,网络通信的底层实现,Netty 都已经帮我们封装好了,我们只需要扩展 ChannelHandler 实现自定义的编解码逻辑即可。 更加人性化的是,Netty 提供了很多开箱即用的解码器,这些

    2024年02月13日
    浏览(34)
  • flutter 视频解码器fijkplayer使用

           本人做视频监控项目的时候,需要去展示视频流到用户端,一开始使用flutter自带的VideoPlayer播放监控视频,一开始没有发现有什么问题,因为使用多的是Android模拟器,一直没有使用iso模拟器或者真机测试能不能播放,直到开发接近尾声,在ios模拟器上测试的时候发现

    2023年04月10日
    浏览(46)
  • ffmpeg中的avs解码器综述

    最近拿了一个avs的视频流,用硬件可以解码,但是ffmpeg自带的却无法解码。 所以研究了一下,首先看ffmpeg的avs解码器: 可以看到avs有两个,第一个是avs 第二个是cavs. 我们先用avs来解码,解码的视频是通过【 avs编码器 】编码的: 结果发现有问题,尺寸本来是640 360,结果被强

    2024年02月08日
    浏览(51)
  • 深入了解Transformer:从编码器到解码器的神经网络之旅

    自2017年问世以来,Transformer模型在自然语言处理(NLP)领域引发了一场革命。它的独特设计和高效性能使其成为了解决复杂语言任务的关键工具。 (1)自注意力机制 Transformer的核心在于自注意力机制。它允许模型在处理每个词时考虑句子中的所有其他词,从而有效捕获长距离依

    2024年01月17日
    浏览(50)
  • 【NLP概念源和流】 06-编码器-解码器模型(6/20 部分)

            在机器翻译等任务中,我们必须从一系列输入词映射到一系列输出词。读者必须注意,这与“序列标记”不同,在“序列标记”中,该任务是将序列中的每个单词映射到预定义的类,如词性或命名实体任务。 作者生成         在上面的

    2024年02月14日
    浏览(45)
  • ffmpeg视频解码器的配置选项含义

    lowres 是 AVCodecContext 结构体中的一个成员变量,用于指定编解码器的降低分辨率级别。 在某些情况下,为了加快编解码的速度或减少计算资源的消耗,可以通过设置 lowres 参数来降低编解码器的分辨率级别。这将导致编解码器在处理视频时使用较低的分辨率,从而减少计算量

    2024年02月22日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包