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;
}
相关推荐
椰萝Yerosius1 小时前
[题解]2024CCPC郑州站——Z-order Curve
c++·算法
滨HI04 小时前
C++ opencv简化轮廓
开发语言·c++·opencv
学习路上_write4 小时前
FREERTOS_互斥量_创建和使用
c语言·开发语言·c++·stm32·单片机·嵌入式硬件
闻缺陷则喜何志丹5 小时前
【SOSDP模板 容斥原理 逆向思考】3757. 有效子序列的数量|分数未知
c++·算法·力扣·容斥原理·sosdp·逆向思考
BestOrNothing_20156 小时前
一篇搞懂 C++ 重载:函数重载 + 运算符重载,从入门到会用(含 ++、<<、== 实战)
c++·函数重载·运算符重载·operator·前置后置++·重载与重写
2501_941144426 小时前
Python + C++ 异构微服务设计与优化
c++·python·微服务
程序猿编码6 小时前
PRINCE算法的密码生成器:原理与设计思路(C/C++代码实现)
c语言·网络·c++·算法·安全·prince
charlie1145141917 小时前
深入理解C/C++的编译链接技术6——A2:动态库设计基础之ABI设计接口
c语言·开发语言·c++·学习·动态库·函数
Cx330❀7 小时前
C++ STL set 完全指南:从基础用法到实战技巧
开发语言·数据结构·c++·算法·leetcode·面试
zmzb01037 小时前
C++课后习题训练记录Day33
开发语言·c++