当前位置: 首页 > article >正文

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;
}

 


http://www.kler.cn/a/488325.html

相关文章:

  • 获取地图文档中的图层列表
  • 网络-ping包分析
  • React Router底层核心原理详解
  • Linux 文件的特殊权限—ACL项目练习
  • 【C++】B2106 矩阵转置
  • 【权限管理】Apache Shiro学习教程
  • Docker入门篇[SpringBoot之Docker实战系列] - 第534篇
  • 商品详情API接口数据解析,API接口系列(示例返回数据(JSON格式))
  • THB6128安森美步进电机驱动芯片
  • leetcode 面试经典 150 题:两数之和
  • 算法题(31):两数之和-输入有序数组
  • 2025年新出炉的MySQL面试题
  • 基于http协议的天气爬虫
  • 计算机视觉算法实战——YOLOv7在姿态识别上的应用
  • 【IDEA】快捷键篇
  • 麦田物语学习笔记:创建DragItem实现物品的拖拽跟随显示
  • 【Flutter】使用ScrollController配合EasyRefresh实现列表预加载:在还未滑动到底部时加载下一页数据
  • 图像传感器的视觉演变与创新
  • Unity自定义编辑器:基于枚举类型动态显示属性
  • sql 函数
  • 联邦大语言模型典型系统: FATE - LLM、FedLLM、FederatedScope - LLM、PrimiHub
  • 【Flink】flink或java异常日志输出不完整问题解决
  • 10步打造完美ASP.NET、Web API和控制台应用程序文件夹结构
  • C语言:构造类型(共用体/联合体,枚举)
  • Apache AGE:基于PostgreSQL的图数据库深度解析
  • Python--正则表达式