基于FFMpeg实现音频mp3/aac/wav解码

这篇具有很好参考价值的文章主要介绍了基于FFMpeg实现音频mp3/aac/wav解码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

编译环境:Ubuntu16.04 64位
交叉编译工具:arm-himix200-linux-gcc

1. ffmpeg源码下载

我这里使用的是ffmpeg-5.1.2.tar.gz,下载地址点击下载地址。

2. 交叉编译

cd /root/
tar zxvf ffmpeg-5.1.2.tar.gz
cd ffmpeg-5.1.2
mkdir output
./configure --cross-prefix=arm-himix200-linux- --enable-cross-compile --target-os=linux --cc=arm-himix200-linux-gcc --arch=arm --prefix=/root/ffmpeg-5.1.2/output --disable-x86asm --disable-debug --disable-doc --disable-zlib  --disable-v4l2-m2m --disable-iconv --disable-network --disable-ffplay --disable-ffprobe --disable-symver --disable-indevs --disable-outdevs --disable-parsers --disable-bsfs --disable-filters --disable-protocols --disable-hwaccels --disable-muxers --disable-demuxers --disable-encoders --disable-decoders --enable-demuxer=aac --enable-demuxer=mp3 --enable-demuxer=wav --enable-decoder=mp3 --enable-decoder=aac --enable-decoder=pcm_alaw --enable-decoder=pcm_bluray --enable-decoder=pcm_dvd --enable-decoder=pcm_f16le --enable-decoder=pcm_f24le --enable-decoder=pcm_f32be --enable-decoder=pcm_f32le --enable-decoder=pcm_f64be --enable-decoder=pcm_f64le --enable-decoder=pcm_lxf --enable-decoder=pcm_mulaw --enable-decoder=pcm_s16be --enable-decoder=pcm_s16be_planar --enable-decoder=pcm_s16le --enable-decoder=pcm_s16le_planar --enable-decoder=pcm_s24be --enable-decoder=pcm_s24daud --enable-decoder=pcm_s24le --enable-decoder=pcm_s24le_planar --enable-decoder=pcm_s32be --enable-decoder=pcm_s32le --enable-decoder=pcm_s32le_planar --enable-decoder=pcm_s64be --enable-decoder=pcm_s64le --enable-decoder=pcm_s8 --enable-decoder=pcm_s8_planar --enable-decoder=pcm_u16be --enable-decoder=pcm_u16le --enable-decoder=pcm_u24be --enable-decoder=pcm_u24le --enable-decoder=pcm_u32be --enable-decoder=pcm_u32le --enable-decoder=pcm_u8 --enable-decoder=pcm_vidc --enable-decoder=pcm_zork --enable-protocol=file --enable-small --enable-muxer=pcm_s16le --extra-cflags="-ffunction-sections -fdata-sections -fsigned-char -Wformat"
make
make install

这样,/root/ffmpeg-5.1.2/output下面就是咱们要的程序,bin目录下ffmpeg可以在开发板上运行,include下是需要的头文件,lib下是需要的静态库,share/ffmpeg/examples是一些可以参考的示例代码。
注意,./configure配置命令可以根据实际的需要进行裁剪,我的项目只需要将mp3、aac和wav解码,因此,只配置了相应的demuxer和decoder。

CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,--gc-sections

GCC链接操作是以section作为最小的处理单元,只要一个section中的某个符号被引用,该section就会被加入到可执行程序中去。因此,GCC在编译时可以使用 -ffunction-sections 和 -fdata-sections 将每个函数或符号创建为一个sections,其中每个sections名与function或data名保持一致。而在链接阶段, -Wl,–gc-sections 指示链接器去掉不用的section(其中-wl, 表示后面的参数 -gc-sections 传递给链接器),这样就能减少最终的可执行程序的大小了。

3. 静态库链接

LIBS += libavdevice.a \
		libavfilter.a \
		libavformat.a \
		libavcodec.a \
		libavutil.a \
		libswresample.a \
		libswscale.a

注意,顺序不要乱,否则会报找不到函数,因为有依赖关系。

不需要用到libavdevice.a(读设备,摄像头或录屏等)、libavfilter.a(加特效,如水印等)和libswscale.a(图像拉伸,像素格式转换等)。

LIBS += libavformat.a \
		libavcodec.a \
		libavutil.a \
		libswresample.a

4. 头文件

#ifndef __AUDIO_DECODER__
#define __AUDIO_DECODER__

#ifdef __cplusplus
extern "C" {
#endif
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/frame.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h"
#ifdef __cplusplus
}
#endif

class CAudioDecoder
{
public:
	CAudioDecoder();
	virtual ~CAudioDecoder(void);

	int DecodeOpen(uint codec);
	int DecodeClose(void);

	int DecodeFrame(uchar *pFrame, int nFrameSize, uchar *pOutBuf, int nBufferSize);
private:
	void Decode(uchar *pOutBuf, int nBufferSize, int &nSize);
	
public:
	static int transcode_pcm(const char* inputFile, const char* outputFile);

private:
	AVCodec *m_Codec;
	AVCodecContext *m_Context;
	AVCodecParserContext *m_Parser;
	AVPacket *m_Packet;
	AVFrame *m_Frame;

	struct SwrContext* m_converCtx;
	AVChannelLayout m_ChannelLayout;
	int m_outBytesPerSample;
	
	uint8_t* m_pData;
	int m_nSize;
};

不知道ffmpeg的头文件为什么没有extern “C”,C++和C在链接时的差异。

5. 音频文件转换


#define TRANSCODE_SAMPLERATE 8000
#define TRANSCODE_FORMAT AV_SAMPLE_FMT_S16
#define TRANSCODE_CHANNELS 1
#define TRANSCODE_NBSAMPLES 4096

/*
	* inputFile        input file name
	* outputFile        output file name
	* return	decode the length of the data
*/
int CAudioDecoder::transcode_pcm(const char* inputFile, const char* outputFile)
{
	CFile file;
	bool bRet = file.Open(outputFile, CFile::modeCreate | CFile::modeWrite);
	if (!bRet)
		return -1;

	int ret = -1;
	AVCodec* codec = NULL;
	AVCodecContext* codecContext = NULL;
	AVCodecParameters* codecpar = NULL;
	AVFrame* frame = NULL;	
	AVPacket *pPacket = NULL;
	struct SwrContext* converCtx = NULL;
	uint8_t** data = NULL;
	
	int streamindex = 0;
	int outBytesPerSample = 0;
	int allocsize = 0;
	AVChannelLayout out_ch_layout = AV_CHANNEL_LAYOUT_MONO;
	
	AVFormatContext* frameContext = avformat_alloc_context();
	if (frameContext == NULL) {
		goto cleanup;
	}

	if (avformat_open_input(&frameContext, inputFile, NULL, NULL) != 0) {
		goto cleanup;
	}

	if (avformat_find_stream_info(frameContext, NULL) < 0) {
		goto cleanup;
	}

	streamindex = av_find_best_stream(frameContext, AVMEDIA_TYPE_AUDIO, -1, -1, (const AVCodec**)&codec, -1);
	if (!codec) {
		goto cleanup;
	}
	
	codecContext = avcodec_alloc_context3(codec);
	codecpar = frameContext->streams[streamindex]->codecpar;
	avcodec_parameters_to_context(codecContext, codecpar);
	if (avcodec_open2(codecContext, codec, NULL) < 0) {
		goto cleanup;
	}

	outBytesPerSample = TRANSCODE_CHANNELS * av_get_bytes_per_sample(TRANSCODE_FORMAT);
	frame = av_frame_alloc();
	swr_alloc_set_opts2(&converCtx, &out_ch_layout, TRANSCODE_FORMAT, TRANSCODE_SAMPLERATE, &codecContext->ch_layout, codecContext->sample_fmt, codecContext->sample_rate, 0, NULL);
	swr_init(converCtx);
	data = (uint8_t**)av_calloc(1, sizeof(*data));
	allocsize = av_samples_alloc(data, NULL, TRANSCODE_CHANNELS, TRANSCODE_NBSAMPLES, TRANSCODE_FORMAT, 0);

	pPacket = av_packet_alloc();
	while (av_read_frame(frameContext, pPacket) >= 0) {
		if (pPacket->stream_index != streamindex)
			continue;

		if (avcodec_send_packet(codecContext, pPacket) < 0) {
			goto cleanup;
		}

		while (avcodec_receive_frame(codecContext, frame) >= 0) {
			ret = swr_convert(converCtx, data, allocsize, (const uint8_t**)frame->data, frame->nb_samples);
			if (ret > 0)
				file.Write(data[0], ret * outBytesPerSample);
		}
		av_packet_unref(pPacket);
	}

	while ((ret = swr_convert(converCtx, data, allocsize, NULL, 0)) > 0) {
		file.Write(data[0], ret * outBytesPerSample);
	}

cleanup:
	if (frameContext) {
		avformat_close_input(&frameContext);
		avformat_free_context(frameContext);
	}

	if (codecContext)
		avcodec_free_context(&codecContext);

	if (pPacket)
		av_packet_free(&pPacket);

	if (data)
		av_freep(&data[0]);
	
	av_freep(&data);

	if (frame)
		av_frame_free(&frame);
	
	if (converCtx)
		swr_free(&converCtx);

	if (file.IsOpened()) {
		ret = file.GetLength();
		file.Close();
	}
	
	return ret;
}

注意,CFile是我的项目中的文件类,可以替换成文件IO的API。

6. 音频实时解码

以aac为例,由于用到了aac的parser,在交叉编译ffmpeg时选项需要添加选项:

--enable-parser=aac

类似,如果需要支持mp3的解码,在交叉编译ffmpeg时选项需要添加选项:

--enable-parser=mp3

其他格式类似。

#include "AudioDecoder.h"

CAudioDecoder::CAudioDecoder()
{
	m_Codec = NULL;
	m_Context = NULL;
	m_Parser = NULL;
	m_Packet = NULL;
	m_Frame = NULL;

	m_converCtx = NULL;
	m_ChannelLayout = AV_CHANNEL_LAYOUT_MONO;
	m_outBytesPerSample = 0;
	
	m_pData = NULL;
	m_nSize = 0;
}

CAudioDecoder::~CAudioDecoder(void)
{
	DecodeClose();
}

int CAudioDecoder::DecodeOpen(uint codec)
{
	int ret = -1;

	switch (codec) {
		case CODEC_AAC:
			m_Codec = (AVCodec *)avcodec_find_decoder(AV_CODEC_ID_AAC);
			break;
		default:
			m_Codec = NULL;
			break;
	}

	if (!m_Codec) {
		fprintf(stderr, "Codec not found\n");
		goto error;
	}
	
	m_Context = avcodec_alloc_context3(m_Codec);
	if (!m_Context) {
		fprintf(stderr, "Could not allocate audio codec context\n");
		goto error;
	}
	
	ret = avcodec_open2(m_Context, m_Codec, NULL);
	if (ret < 0) {
		fprintf(stderr, "Could not open codec\n");
		goto error;
	}

	m_Parser = av_parser_init(m_Codec->id);
	if (!m_Parser) {
		fprintf(stderr, "Parser not found\n");
		goto error;
	}

	m_Packet = av_packet_alloc();
	if (!m_Packet) {
		fprintf(stderr, "Could not allocate audio packet\n");
		goto error;
	}
		
	m_Frame = av_frame_alloc();
	if (!m_Frame) {
		fprintf(stderr, "Could not allocate audio frame\n");
		goto error;
	}

	m_outBytesPerSample = TRANSCODE_CHANNELS * av_get_bytes_per_sample(TRANSCODE_FORMAT);
	m_nSize = TRANSCODE_NBSAMPLES * m_outBytesPerSample;
	m_pData = (uint8_t *)av_malloc(m_nSize);

	return 0;
	
error:
	DecodeClose();
	return ret;
}

int CAudioDecoder::DecodeClose(void)
{
	av_freep(&m_pData);

	if (m_Frame)
	    av_frame_free(&m_Frame);

	if (m_converCtx)
		swr_free(&m_converCtx);

	if (m_Packet)
	    av_packet_free(&m_Packet);

	if (m_Parser)
    	av_parser_close(m_Parser);

	if (m_Context)
    	avcodec_free_context(&m_Context);


	return 0;
}

int CAudioDecoder::DecodeFrame(uchar *pFrame, int nFrameSize, uchar *pOutBuf, int nBufferSize)
{
	if (pFrame == NULL || nFrameSize == 0 || pOutBuf == NULL || nBufferSize == 0) {
		return -1;
	}

	int nSize = 0;
    uint8_t *data = pFrame;
    int data_size = nFrameSize;
	
	while (data_size > 0) {
        int ret = av_parser_parse2(m_Parser, m_Context, &m_Packet->data, &m_Packet->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
		if (ret < 0) {
			fprintf(stderr, "Error while parsing\n");
			break;
		}

		if (m_Packet->size > 0) {
			data += ret;
			data_size -= ret;

			Decode(pOutBuf, nBufferSize, nSize);
		} else {
			break;
		}
	}

	return nSize;
}

void CAudioDecoder::Decode(uchar *pOutBuf, int nBufferSize, int &nSize)
{
	int ret = avcodec_send_packet(m_Context, m_Packet);
	if (ret < 0) {
		return ;
	}
	
	swr_alloc_set_opts2(&m_converCtx, &m_ChannelLayout, TRANSCODE_FORMAT, TRANSCODE_SAMPLERATE, &m_Context->ch_layout, m_Context->sample_fmt, m_Context->sample_rate, 0, NULL);
	swr_init(m_converCtx);

	while (avcodec_receive_frame(m_Context, m_Frame) >= 0) {		
		ret = swr_convert(m_converCtx, &m_pData, m_nSize, (const uint8_t**)m_Frame->data, m_Frame->nb_samples);
		if (ret > 0) {
			if (nBufferSize < nSize + ret * m_outBytesPerSample) {
				fprintf(stderr, "nBufferSize=[%d] too small!!!\n", nBufferSize);
				break;
			}
			
			memcpy(pOutBuf + nSize, m_pData, ret * m_outBytesPerSample);
			nSize += ret * m_outBytesPerSample;
		}
	}

	while ((ret = swr_convert(m_converCtx, &m_pData, m_nSize, NULL, 0)) > 0) {
		if (nBufferSize < nSize + ret * m_outBytesPerSample) {
			fprintf(stderr, "nBufferSize=[%d] too small!!!\n", nBufferSize);
			break;
		}
		memcpy(pOutBuf + nSize, m_pData, ret * m_outBytesPerSample);
		nSize += ret * m_outBytesPerSample;
	}

	return ;
}

注意:CODEC_AAC是我的项目中的aac格式宏,替换成自己的aac/mp3,AV_CODEC_ID_AAC换成相应的枚举即可完成其他格式的解码。

7. 使用说明

7.1 文件转换

int nPcmSize = CAudioDecoder::transcode_pcm(inputFile, outputFile);

inputFile :需要解码的文件名
outputFile :解码后的文件名
nPcmSize :解码后的文件长度,<=0 失败,>0 成功

7.2 实时解码

创建AAC解码器

CAudioDecoder *decoder = new CAudioDecoder();
decoder->DecodeOpen(CODEC_AAC);

解码

int OutSize = decoder->DecodeFrame(pFrame, nFrameSize, pOutBuf, nBufferSize);	

销毁解码器

delete decoder;
decoder = NULL;

解码数据,可以传输m.n帧,m>=0 n>=0
pFrame 输入,待解码的数据
nFrameSize 输入,待解码的数据长度
pOutBuf 输入输出,解码后数据,缓冲区由调用者分配
nBufferSize 输入,传入的缓冲区pOutBuf长度
return 返回解码后缓冲区已使用的数据长度,如果传入数据小于1帧,本地调用会返回0,直到下一次输入的数据达到1帧

注意:对于一组连续的音频数据,不要重复调用创建和销毁解码器。文章来源地址https://www.toymoban.com/news/detail-675236.html

到了这里,关于基于FFMpeg实现音频mp3/aac/wav解码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • ffmpeg合成mp3音频,解决音频属性不一致问题

    命令格式及参数格式说明 amr2mp3.MP3输出为 原本就是mp3的输出为: 对比可发现Hz不一致 将amr转mp3是设置Hz和bitrate,将两个文件属性保持一致 合成MP3 ffmpeg对以上参数介绍 因为声道数不一样导致 通过ffprobe -show_frames 20240201112247.mp3查看通道数 arm转mp3时将通道数ac设置为1即可 中文

    2024年02月20日
    浏览(39)
  • Java Mp3转化WAV/PCM音频数据,解码详细解析,提取每一帧数据集合/比特流/播放,一行代码!

    大家好!我是原子君 1 .因为Java本身只支持,wav,缺少mp3的解码器,所以Java自带的无法对mp3进行处理,这种 MPEG-*音频有损压缩标准编码 ,更不要说使用Java的音频格式和音频流就可以解决。 2 .所以本次转换需要使用到colorful1.1这种纯Java-Pc可跨平台的工具框架。 注意:colorful只支持

    2024年02月15日
    浏览(50)
  • 使用FFMPEG分离mp4/flv文件中的264视频和aac音频

    ffmpeg 4.4 一个MP4或flv格式的视频文件 大致分为以下几个简单步骤: 1.使用avformat_open_input 函数打开文件并初始化结构AVFormatContext 2.查找是否存在音频和视频信息 3.构建一个h264_mp4toannexb比特流的过滤器,用来给视频avpaket包添加头信息 4.打开2个输出文件(音频, 视频) 5.循环读

    2024年02月15日
    浏览(49)
  • node使用fluent-ffmpeg把webm格式的音频转成mp3

    下载并安装ffmpeg http://ffmpeg.org/download.html?aemtn=tg-on https://www.gyan.dev/ffmpeg/builds/  配置环境变量:  查询版本 装包:    

    2024年02月16日
    浏览(51)
  • Java使用ffmpeg实现视频剪切、mp3剪切

      直接运行代码即可:   剪切前      剪切后

    2024年02月16日
    浏览(37)
  • 音频文件PCM、WAV、MP3的区别以及文件合并

    采样率即采样频率,指的一秒内的采样次数,它反映了采样点之间的间隔大小。常说的 44.1KHz 采样率,也即 1 秒采集了 44100 个样本。间隔越小,丢失的信息越少,数字声音就越逼真细腻,要求的存储量也就越大。由于计算机的工作速度和存储容量有限,而且人耳的听觉上限为

    2024年02月15日
    浏览(50)
  • mp3怎样才能转换成wav格式?音频互相转换的方法

    一,什么是WAV WAV,全称为波形音频文件(Waveform Audio File Format),是一种由微软公司和IBM公司联合开发的音频文件格式。自1991年问世以来,WAV格式因其无损的音频质量和广泛的兼容性,成为了多媒体应用中不可或缺的一部分。本文将从WAV格式的定义、特点、应用以及与其他音

    2024年04月14日
    浏览(58)
  • FFmpeg从入门到入魔(3):提取MP4中的H.264和AAC

        最近在开发中遇到了一个问题,即无法提取到MP4中H264流的关键帧进行处理,且保存到本地的AAC音频也无法正常播放。经过调试分析发现,这是由于解封装MP4得到的H264和AAC是ES流,它们缺失解码时必要的 起始码 / SPS / PPS 和 adts头 。 1. MP4格式解析 1.1 MP4简介  MP4封装格式是

    2023年04月16日
    浏览(48)
  • ExoPlayer(AndroidX Media3) 扩展ffmpeg实现音频软解码

    1.Ubuntu 20.04.4 LTS 2.AndroidNDK版本r26C 3.AndroidStudio 2023.1.1(配置好SDK和JDK 17.0.10) 4.ffmpeg6.0源码 5.ExoPlayer源码,AndroidX Media release分支版本 目前官方已废弃Exopler2,代码已经迁移到AndroidX Media,下载完成设置FFMPEG_MODULE_PATH变量 1. git clone https://github.com/androidx/media 2. cd media FFMPEG_MODULE_PATH

    2024年04月12日
    浏览(123)
  • Node.js脚本项目合集(一):Node.js+FFmpeg实现批量从B站导出离线缓存视频到mp4格式,mp4转mp3,实现听歌自由

    最近被一首JISOO的FLOWER歌洗脑,但碍于版权原因,只能在B站上看mv视频,盯着尬舞听歌着实有些尴尬,突发奇想,如果能将视频中的音频和视频分开不就能只听音乐,不用看尴尬的舞蹈吗?刚好手机上有不少B站本地的学习视频想导入到电脑上看,可是B站下载的格式.m4s文件,

    2024年02月07日
    浏览(74)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包