ffmpeg7.0 aac转pcm

#pragma once
#define __STDC_CONSTANT_MACROS
#define _CRT_SECURE_NO_WARNINGS

extern "C"
{
#include "libavcodec/avcodec.h"
}

//缓冲区大小(缓存5帧数据)
#define AUDIO_INBUF_SIZE 40960  
/*
	name   depth
	u8        8
	s16      16
	s32      32
	flt      32
	dbl      64
	u8p       8
	s16p     16
	s32p     32
	fltp     32
	dblp     64
	s64      64
	s64p     64
	//此代码解码的音频文件格式如下:
	//AAC文件(一帧1024字节),双声道(2),FLTP(32位,4字节)
	//AAC文件 frame_size 和 nb_samples 大小均为1024
	//一帧音频所占字节大小
	//1024*2*4=8192字节
*/
#define AUDIO_REFILL_THRESH 8192

using namespace std;

#define INPUT_FILE_NAME "d:\\123.aac"
#define OUTPUT_FILE_NAME "d:\\1111.pcm"


static int get_format_from_sample_fmt(const char** fmt,	enum AVSampleFormat sample_fmt)
{
	struct sample_fmt_entry {
		enum AVSampleFormat sample_fmt; 
		const char* fmt_be, * fmt_le;
	} sample_fmt_entries[] = {
		{ AV_SAMPLE_FMT_U8,  "u8",    "u8"    },
		{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },
		{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },
		{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
		{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
	};
	*fmt = NULL;

	for (int i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
		struct sample_fmt_entry* entry = &sample_fmt_entries[i];
		if (sample_fmt == entry->sample_fmt) {
			*fmt = AV_NE(entry->fmt_be, entry->fmt_le);
			return 0;
		}
	}

	av_log(NULL, AV_LOG_ERROR, "sample format %s is not supported as output format\n", av_get_sample_fmt_name(sample_fmt));
	return -1;
}

static void decode(AVCodecContext* pCodecContext, AVFrame* pFrame, AVPacket* pPacket, FILE* pFile)
{
	int ret = avcodec_send_packet(pCodecContext, pPacket);
	if (ret < 0) {
		av_log(NULL, AV_LOG_ERROR, "发送数据包到解码器出错。\n");
		exit(1);
	}

	while (ret >= 0) {
		ret = avcodec_receive_frame(pCodecContext, pFrame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
			return;
		}
		else if (ret < 0) {
			av_log(NULL, AV_LOG_ERROR, "Error sending a packet for decoding.\n");
			exit(1);
		}
		
		//获取每个采样点当中每个声道的大小
		int nDataSize = av_get_bytes_per_sample(pCodecContext->sample_fmt);
		if (nDataSize < 0) {
			av_log(NULL, AV_LOG_ERROR, "Failed to calculate data size.\n");
			exit(1);
		}

		//遍历采样点
		for (int i = 0; i < pFrame->nb_samples; i++) {
			//遍历声道
			for (int ch = 0; ch < pCodecContext->ch_layout.nb_channels; ch++) {
				fwrite(pFrame->data[ch] + nDataSize * i, 1, nDataSize, pFile);
			}
		}
	}
}

int main(int argc, char* argv[])
{
	//初始化inbuf数字默认值
	uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE] = {0};

	//获取解码器(此处需要读取的文件是AAC,故)
	const AVCodec* pCodecOfAAC = avcodec_find_decoder(AV_CODEC_ID_AAC);
	if (!pCodecOfAAC) {
		av_log(NULL, AV_LOG_ERROR, "Codec not found.\n");
		exit(1);
	}

	//注册解析器
	AVCodecParserContext* pCodecParserParser = av_parser_init(pCodecOfAAC->id);
	if (!pCodecParserParser) {
		av_log(NULL, AV_LOG_ERROR, "parser not found.\n");
		exit(1);
	}

	//分配解析器上下文
	AVCodecContext* pCodecContextOfAAC = avcodec_alloc_context3(pCodecOfAAC);
	if (!pCodecContextOfAAC) {
		av_log(NULL, AV_LOG_ERROR, "Could not allocate video codec context.\n");
		exit(1);
	}

	//打开解码器
	if (avcodec_open2(pCodecContextOfAAC, pCodecOfAAC, NULL) < 0) {
		av_log(NULL, AV_LOG_ERROR, "Could not open codec.\n");
		exit(1);
	}


	//分配AVPacket
	AVPacket* pPacket = av_packet_alloc();
	if (!pPacket) {
		exit(1);
	}

	//分配AVFrame
	AVFrame* pFrame = av_frame_alloc();
	if (!pFrame) {
		exit(1);
	}

	//打开输入文件
	FILE* ifile = fopen(INPUT_FILE_NAME, "rb");
	if (!ifile) {
		av_log(NULL, AV_LOG_ERROR, "Could not open \s.\n", INPUT_FILE_NAME);
		exit(1);
	}

	//打开输入文件
	FILE* ofile = fopen(OUTPUT_FILE_NAME, "wb+");
	if (!ofile) {
		av_log(NULL, AV_LOG_ERROR, "Could not open \s.\n", OUTPUT_FILE_NAME);
		exit(1);
	}

	//从输入流 ifile 读取数据到 inbuf 所指向的数组中
	uint8_t* data = inbuf;
	size_t nDataSize = fread(inbuf, 1, AUDIO_INBUF_SIZE, ifile);

	while (nDataSize > 0) {
		//使用注册的解析器 parser 把数据分割成帧
		int nRet = av_parser_parse2(pCodecParserParser, pCodecContextOfAAC, &pPacket->data, &pPacket->size, data, nDataSize, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
		if (nRet < 0) {
			fprintf(stderr, "Error while parsing\n");
			exit(1);
		}
		//根据使用情况重置数据位置
		data += nRet;
		nDataSize -= nRet;

		//送往解码
		if (pPacket->size) {
			decode(pCodecContextOfAAC, pFrame, pPacket, ofile);
		}
			

		//判断缓存区剩余数据是否小于一帧音频大小
		//小于的话从文件继续读取,之后在送往解码
		if (nDataSize < AUDIO_REFILL_THRESH) {
			memmove(inbuf, data, nDataSize);
			data = inbuf;
			int nLen = fread(data + nDataSize, 1, AUDIO_INBUF_SIZE - nDataSize, ifile);
			if (nLen > 0) {
				nDataSize += nLen;
			}
		}
	}

	//flush 解码器
	decode(pCodecContextOfAAC, pFrame, NULL, ofile);

	//此时就已经解码完了,我们稍后使用ffplay播放下音频
	//解码出来的pcm数据是没有这些基础数据的,我们需要从元数据获取
	//打印下基本信息

	//声道数
	printf("channels: %d \n", pCodecContextOfAAC->ch_layout.nb_channels);

	//采样率
	printf("sample_rate: %d  \n", pCodecContextOfAAC->sample_rate);

	//一帧音频所占字节代销
	printf("buffer: %d  \n", av_samples_get_buffer_size(NULL, pCodecContextOfAAC->ch_layout.nb_channels, pCodecContextOfAAC->frame_size, pCodecContextOfAAC->sample_fmt, 1));
	
	//采样格式
	enum AVSampleFormat sfmt = pCodecContextOfAAC->sample_fmt;
	printf("sample_fmt: %s  \n", av_get_sample_fmt_name(sfmt));

	//如果为planar,转换为packed格式
	if (av_sample_fmt_is_planar(sfmt)) {
		const char* packed = av_get_sample_fmt_name(sfmt);
		sfmt = av_get_packed_sample_fmt(sfmt);
	}

	const char* fmt = NULL;
	if (get_format_from_sample_fmt(&fmt, sfmt) < 0) {
		av_log(NULL, AV_LOG_ERROR, "Could not get forma \s.\n", av_get_sample_fmt_name(sfmt));
		exit(1);
	}

	//资源释放
	fclose(ifile);
	fclose(ofile);

	av_parser_close(pCodecParserParser);
	avcodec_free_context(&pCodecContextOfAAC);
	av_frame_free(&pFrame);
	av_packet_free(&pPacket);

	return 0;
}
相关推荐
Growthofnotes1 小时前
C++—14、C++ 中的指针最基础的原理
开发语言·c++
bohu831 小时前
ros2笔记-4.3 用C++做一个巡逻海龟
c++·笔记·ros2·服务通信
F-2H3 小时前
C语言:构造类型(共用体/联合体,枚举)
java·linux·c语言·开发语言·数据结构·c++·算法
想成为cpp糕手4 小时前
stack&queue
c++
秋知叶i5 小时前
【轻松学C:编程小白的大冒险】--- 选择 开发工具(IDE)Dev-c++ 03
c语言·开发语言·c++
daily_23335 小时前
c++领域展开第十幕——类和对象(内存管理——c/c++内存分布、c++内存管理方式、new/delete与malloc/free区别)超详细!!!!
c语言·c++
就叫飞六吧5 小时前
C语言中,`extern` 和 `#include`
c语言·c++·算法
涛ing6 小时前
12. C语言 数组与指针(深入理解)
linux·c语言·开发语言·数据结构·c++·算法·ubuntu
黑果果的思考8 小时前
C++例程:使用I/O模拟IIC接口(6)
开发语言·c++