音频demo:使用fdk-aac将PCM数据编码成aac数据

1、README

a. 编译
编译demo

本demo是使用的开源项目fdk-aac将PCM数据编码成aac音频文件。由于提供的.a静态库是在x86_64的机器上编译的,所以默认情况下仅支持该架构的主机上编译运行。

$ make
编译fdk-aac(可选)

如果想要在其他架构的CPU上编译运行,可以使用以下命令(脚本)编译fdk-aac[下载地址1][下载地址2]得到相应的库文件进行替换:

#!/bin/bash

tar xzf fdk-aac-2.0.2.tar.gz
cd fdk-aac-2.0.2/
./configure --prefix=$PWD/_install # --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc
make -j96
make install
b. 使用
$ ./pcm2aac -h
$ ./pcm2aac --help
$ ./pcm2aac -i ./audio/test_8000_16_1.pcm -r 8000 -b 16 -c 1 -o out_8khz_1ch.aac
$ ./pcm2aac --input_pcmfile=./audio/test_44100_16_2.pcm --sample_rate=44100 --sample_bits=16 --channels=2 --output_aacfile=out_44.1khz_2ch.aac

其中,fdk-aac对输入的编码数据格式做了些说明:网页链接

aac params

c. 参考文章

【格式说明】

  • AAC文件格式解析_cloud 的学习时代-CSDN博客_aac

  • 从零开始写一个RTSP服务器(5)RTP传输AAC_JT同学的博客-CSDN博客

  • 音频编码格式介绍-AAC - 简书

【编码实现】

  • 使用fdkaac编码_程序人生-CSDN博客

  • 【音频编码】AAC编码之FDK AAC_CWB的博客-CSDN博客_fdkaac

d. demo目录架构
.
├── audio
│   ├── out_44.1khz_2ch.aac
│   ├── out_8khz_1ch.aac
│   ├── test_44100_16_2.pcm
│   └── test_8000_16_1.pcm
├── docs
│   ├── AAC文件格式解析_cloud 的学习时代-CSDN博客_aac.mhtml
│   ├── 从零开始写一个RTSP服务器(5)RTP传输AAC_JT同学的博客-CSDN博客.mhtml
│   ├── 使用fdkaac编码_程序人生-CSDN博客.mhtml
│   ├── 【音频编码】AAC编码之FDK AAC_CWB的博客-CSDN博客_fdkaac.mhtml
│   └── 音频编码格式介绍-AAC - 简书.mhtml
├── include
│   └── fdk-aac
│       ├── aacdecoder_lib.h
│       ├── aacenc_lib.h
│       ├── FDK_audio.h
│       ├── genericStds.h
│       ├── machine_type.h
│       └── syslib_channelMapDescr.h
├── lib
│   └── libfdk-aac.a
├── main.c
├── Makefile
└── README.md

2、主要代码片段

main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>


#include "fdk-aac/aacenc_lib.h"


//#define DEBUG(fmt, args...)
#define DEBUG(fmt, args...) 	printf(fmt, ##args)


void print_usage(const char *process)
{
	printf("sample: \n"
		   "\t %s -h\n"
		   "\t %s --help\n"
		   "\t %s -i ./audio/test_8000_16_1.pcm -r 8000 -b 16 -c 1 -o out_8khz_1ch.aac\n"
		   "\t %s --input_pcmfile=./audio/test_44100_16_2.pcm --sample_rate=44100 --sample_bits=16 --channels=2 --output_aacfile=out_44.1khz_2ch.aac\n",
		   process, process, process, process);
}


int main(int argc, char *argv[])
{
	/* 输入/输出文件 */
	FILE *fpPcm = NULL;
	FILE *fpAac = NULL;
	char pcmFileName[128] = {0};
	char aacFileName[128] = {0};

	/* PCM参数 */
	unsigned int u32PcmSampleRate = 0; // 采样率
	unsigned int u32PcmSampleBits = 0; // 采样位数
	unsigned int u32PcmChannels   = 0; // 声道数

	/* aac编码器 */
	HANDLE_AACENCODER aacEncHandle = NULL; // HANDLE_AACENCODER其实是一个结构体指针
	AACENC_InfoStruct aacEncInfoSt = {0};
	AACENC_ERROR aacErrNum = AACENC_OK; // AACENC_OK:0

	/* 编码相关参数 */
	unsigned int u32PcmInBufBytes = 0; 	// 编码时需要传入的PCM数据大小(字节数)
	unsigned int u32AacOutBufMaxBytes = 0; // 编码后得到一帧aac数据最大的大小(字节数)
	unsigned char *pu8PcmInBuf   = NULL; // 读取pcm并传递进去编码的缓存指针,后面根据编码器传出参数malloc分配
	unsigned char *pu8AacEncBuf  = NULL; // 编码得到的aac缓存,后面根据编码器传出参数malloc分配


	/* 判断输入参数 */
	if(argc == 1)
	{
		print_usage(argv[0]);
		return -1;
	}	

	/* 解析命令行参数 */
	char option = 0;
	int option_index = 0;
	char *short_options = "hi:r:b:c:o:";
	struct option long_options[] =
	{
		{"help",          no_argument,       NULL, 'h'},
		{"input_pcmfile", required_argument, NULL, 'i'},
		{"sample_rate",   required_argument, NULL, 'r'},
		{"sample_bits",   required_argument, NULL, 'b'},
		{"channels",      required_argument, NULL, 'c'},
		{"output_aacfile",required_argument, NULL, 'o'},
		{NULL,            0,                 NULL,  0 },
	};
	while((option = getopt_long_only(argc, argv, short_options, long_options, &option_index)) != -1)
	{
		switch(option)
		{
			case 'h':
				print_usage(argv[0]);
				return 0;
			case 'i':
				strncpy(pcmFileName, optarg, 128);
				break;
			case 'r':
				u32PcmSampleRate = atoi(optarg);
				break;
			case 'c':
				u32PcmChannels = atoi(optarg);
				break;
			case 'b':
				u32PcmSampleBits = atoi(optarg);
				break;
			case 'o':
				strncpy(aacFileName, optarg, 128);
				break;
			defalut:
				printf("Unknown argument!\n");
				break;
		}
 	}
	printf("\n**************************************\n"
		   "input: \n"
		   "\t file name: %s\n"
		   "\t sample rate: %d Hz\n"
		   "\t sample bits: %d bits\n"
		   "\t channels: %d\n"
		   "\t bits per second: %d bps\n"
		   "output: \n"
		   "\t file name: %s\n"
		   "**************************************\n\n",
		   pcmFileName, u32PcmSampleRate, u32PcmSampleBits, u32PcmChannels,
		   u32PcmSampleRate*u32PcmSampleBits*u32PcmChannels, aacFileName);

	/* 先打开输入/输出文件 */
	fpPcm = fopen(pcmFileName, "rb");
	if(fpPcm == NULL)
	{
		char errMsg[128] = {0};
		snprintf(errMsg, 128, "open file(%s) error", pcmFileName);
		perror(errMsg);
		return -1;
	}
	fpAac = fopen(aacFileName, "wb");
	if(fpAac == NULL)
	{
		char errMsg[128] = {0};
		snprintf(errMsg, 128, "open file(%s) error", aacFileName);
		perror(errMsg);
		return -1;
	}

	/* AAC编码 1/8:打开编码器,传出编码器句柄 */
	aacErrNum = aacEncOpen(&aacEncHandle, 0, u32PcmChannels);
	if(aacErrNum != AACENC_OK)
	{
		printf("Open aac encoder error!\n");
		goto error_exit1;
	}

	/* AAC编码 2/8:配置/初始化编码器 */
	// 配置编码器 a/b:设置参数
	aacErrNum  = aacEncoder_SetParam(aacEncHandle, AACENC_AOT, AOT_AAC_LC); 	// Audio object type, 选择输出规格
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_SBR_MODE, 1); 		// Spectral Band Replication,是否使能SBR技术,-1:自动配置(默认) 0:关闭 1:开启
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_SAMPLERATE, u32PcmSampleRate); // Audio input data sampling rate
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_CHANNELMODE, (u32PcmChannels == 1) ? MODE_1 : MODE_2); // 声道模式,还有多种模式,这里只列出2种
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_CHANNELORDER, 1); 	// 输入音频数据通道排序方案,0: MPEG频道排序(默认) 1: WAVE文件格式通道排序
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_BITRATEMODE, 5); 		// 比特率模式,0:CBR  1~5:VBR(数值越大动态码率越高)
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_BITRATE, 128000); 	// 设置比特率大小,只有AACENC_BITRATEMODE设置为静态码率CBR时生效,VBR时忽略
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_TRANSMUX, TT_MP4_ADTS); // 传输类型,TT_MP4_ADIF/TT_MP4_ADTS/TT_MP4_LATM_MCP1...
	aacErrNum |= aacEncoder_SetParam(aacEncHandle, AACENC_AFTERBURNER, 1); 		// “加力燃烧室”,提高音质,0:关闭(默认) 1:开启。官方推荐内存和性能足够的话开启
	// 配置编码器 b/b:设置到编码器里面去
	aacErrNum |= aacEncEncode(aacEncHandle, NULL, NULL, NULL, NULL);
	if(aacErrNum != AACENC_OK)
	{
		printf("Configure aac encoder error!\n");
		goto error_exit1;
	}

	/* AAC编码 3/8:获取编码器信息,从这里得到输入PCM缓存应该设置多大、输出最大的aac字节数 */
	aacErrNum = aacEncInfo(aacEncHandle, &aacEncInfoSt);
	if(aacErrNum != AACENC_OK)
	{
		printf("Get aac encoder info error!\n");
		goto error_exit2;
	}

	// 根据上面获得的编码器信息得到一些比较重要的参数
	u32PcmInBufBytes = aacEncInfoSt.frameLength * u32PcmSampleBits/8 * u32PcmChannels;
	u32AacOutBufMaxBytes = aacEncInfoSt.maxOutBufBytes;

	DEBUG("PCM should in bytes: %d \t AAC out max bytes: %d\n", u32PcmInBufBytes, u32AacOutBufMaxBytes);

	/* 根据上面打开编码器信息分配对应大小的缓存 */
	pu8PcmInBuf  = (unsigned char*)malloc(u32PcmInBufBytes);
	pu8AacEncBuf = (unsigned char*)malloc(u32AacOutBufMaxBytes);


	/* 循环从文件中读取PCM数据编码出aac数据写入到文件中 */
	while(1)
	{
		/* aac编码一帧数据用到的参数 */
		AACENC_BufDesc inPcmBufDesc = {0};
		AACENC_BufDesc outAacBufDesc = {0};
		AACENC_InArgs inArgs = {0};
		AACENC_OutArgs outArgs = {0};
		int inIdentifier = IN_AUDIO_DATA;
		int outIdentifier = OUT_BITSTREAM_DATA;
		//int inElsize = 2;
		//int outElsize = 1;
		int inElsize = sizeof(INT_PCM); // 参考aacenc_lib.h:260示例
		int outElsize = sizeof(UCHAR);

		/* AAC编码 4/8:填充编码器需要的参数,包括编码的pcm数据地址,大小等 */
		int s32ReadPcmBytes = fread(pu8PcmInBuf, 1, u32PcmInBufBytes, fpPcm);
		if(s32ReadPcmBytes <= 0)
		{
			break;
		}

		/* AAC编码 5/8:填充编码器需要的参数,包括编码的pcm数据地址,大小等 */
		inPcmBufDesc.numBufs = 1;
		inPcmBufDesc.bufs = (void **)&pu8PcmInBuf;
		inPcmBufDesc.bufferIdentifiers = &inIdentifier;
		inPcmBufDesc.bufSizes = &s32ReadPcmBytes;
		inPcmBufDesc.bufElSizes = &inElsize;

		inArgs.numInSamples = (s32ReadPcmBytes <= 0) ? -1 : s32ReadPcmBytes/2;

		outAacBufDesc.numBufs = 1;
		outAacBufDesc.bufs = (void **)&pu8AacEncBuf;
		outAacBufDesc.bufferIdentifiers = &outIdentifier;
		outAacBufDesc.bufSizes = &u32AacOutBufMaxBytes;
		outAacBufDesc.bufElSizes = &outElsize;

		/* AAC编码 6/8:将pcm编码出aac */
		aacErrNum = aacEncEncode(aacEncHandle, &inPcmBufDesc, &outAacBufDesc, &inArgs, &outArgs);
		if(aacErrNum != AACENC_OK)
		{
			printf("Aac encoder encode error!\n");
			goto error_exit3;
		}

		DEBUG("IN(pcm): [buf bytes: %4d] [channels: %d] [sample cnt per channel: %4d]  ==>   OUT(aac): [encode out bytes: %4d] \n",
			  s32ReadPcmBytes, u32PcmChannels, inArgs.numInSamples/u32PcmChannels, outArgs.numOutBytes);

		if(outArgs.numOutBytes == 0)
		{
			continue;
		}

		/* AAC编码 7/8:将编码出的aac数据写入文件 */
		fwrite(pu8AacEncBuf, 1, outArgs.numOutBytes, fpAac);
	}

	printf("\n\033[32m%s ==> %s Success!\033[0m\n", pcmFileName, aacFileName);

error_exit3:

	/* 记得释放内存 */
	free(pu8PcmInBuf);
	free(pu8AacEncBuf);

error_exit2:

	/* AAC编码 8/8:关闭编码器 */
	aacEncClose(&aacEncHandle);

error_exit1:

	fclose(fpPcm);
	fclose(fpAac);

	return 0;
}

3、demo下载地址(任选一个)

  • https://download.csdn.net/download/weixin_44498318/89525141

  • https://gitee.com/linriming/audio_pcm2aac_with_fdk-aac.git

  • https://github.com/linriming20/audio_pcm2aac_with_fdk-aac.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/785300.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【笔记】finalshell中使用nano编辑器GNU

ctrl O 保存 enter 确定 ctrl X 退出 nano编辑 能不用就不用吧 因为我真用不习惯 nano编辑的文件也可以用vim编辑的

VsCode崩溃无法打开解决办法

今天打开电脑 点开vscode突然不能使用了 每次点开都闪退,于是查找解决办法 直接重启电脑 这个方法没有解决问题 删除软件 重新安装软件 软件删干净之前保存下自己的配置文件 比如扩展和settings.json和keybinding.json Mac系统 在 /Users/用户名/.vscode/extensions 和/User…

2024年 春秋杯 网络安全联赛夏季赛 Web方向 题解WirteUp 部分

brother 题目描述&#xff1a;web哥&#xff0c;打点容易提权难。 打点就是最简单的SSTI。 执行下find / -user root -perm -4000 -print 2>/dev/null找一下具备suid权限的命令 /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/bin/chsh /usr/bin/gpasswd /usr/bin/n…

Java-链表反转

题目&#xff1a; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 图示&#xff1a; 输入&#xff1a; head [1,2,3,4,5] 输出&#xff1a; [5,4,3,2,1] 解题思路&#xff1a; 情况一&#xff1a; 只有一个节点或者没有节点 …

C语言 | Leetcode C语言题解之第224题基本计算器

题目&#xff1a; 题解&#xff1a; int calculate(char* s) {int n strlen(s);int ops[n], top 0;int sign 1;ops[top] sign;int ret 0;int i 0;while (i < n) {if (s[i] ) {i;} else if (s[i] ) {sign ops[top - 1];i;} else if (s[i] -) {sign -ops[top - 1…

Python | Leetcode Python题解之第223题矩形面积

题目&#xff1a; 题解&#xff1a; class Solution:def computeArea(self, ax1: int, ay1: int, ax2: int, ay2: int, bx1: int, by1: int, bx2: int, by2: int) -> int:area1 (ax2 - ax1) * (ay2 - ay1)area2 (bx2 - bx1) * (by2 - by1)overlapWidth min(ax2, bx2) - …

挑战杯 opencv python 深度学习垃圾图像分类系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; opencv python 深度学习垃圾分类系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 这是一个较为新颖的竞…

如何把harmonos项目修改为openharmony项目

一开始分不清harmonyos和openharmony&#xff0c;在harmonyos直接下载的开发软件&#xff0c;后面发现不对劲&#xff0c;打脑阔 首先你要安装对应版本的开发软件&#xff0c;鸿蒙开发是由harmonyos和openharmony官网两个的&#xff0c;找到对应的地方下载对应版本的开发软件&…

【链表】- 链表相交

1. 对应力扣题目连接 链表相交 2. 实现思路 链表详情&#xff1a; 考虑使用双指针&#xff1a; 解法一&#xff1a; 具体代码&#xff0c;详见3. 实现案例代码解析&#xff1a; 思路&#xff1a;因为链表按照如图的箭头走向&#xff0c;走的总路程是相等的&#xff0c;一…

数字交流便携式电阻式三相负载组

三相型号选项范围从小型、便携式、低功耗单元到大功率、室内和室外永久电阻负载组。型号标配按钮式手动控制以及 PC 软件控制&#xff0c;为处理复杂的测试应用提供先进的负载曲线解决方案。这些装置适用于各种用途&#xff0c;包括测试发电机、UPS 系统、数据中心电源系统、电…

自定义类TMyLabel继承自QLabel ,实现mouseDoubleClickEvent

自定义类TMyLabel &#xff0c;继承自QLabel TMyLabel 中重新实现了 event 方法&#xff0c;重写了mouseDoubleClickEvent 发射信号 在主窗体中放入TMyLabel组件&#xff0c;将TMyLabel mouseDoubleClickEvent 信号&#xff0c; 绑定到实现方法do_doubleClick()槽函数 TMy…

排序算法(算法篇)

算法之排序算法 排序算法 概念&#xff1a; 我们在的排序工作能在主存中完成的&#xff0c;我们就叫这种算法叫做内部排序不能在主存中完成而必须在磁盘或磁带上完成的排序算法叫做外部排序 冒泡排序 概念&#xff1a; 冒泡排序是一个很简单的排序算法&#xff0c;冒泡排…

【测试】软件测试方案(2024模板方案套用)

1. 引言 1.1. 编写目的 1.2. 项目背景 1.3. 读者对象 1.4. 参考资料 1.5. 术语与缩略语 2. 测试策略 2.1. 测试完成标准 2.2. 测试类型 2.2.1. 功能测试 2.2.2. 性能测试 2.2.3. 安全性与访问控制测试 2.3. 测试工具 3. 测试技术 4. 测试资源 4.1. 人员安排 4.2. 测试环境 4.2.…

STM32-按键及传感器模块

本内容是基于江协科技STM32视频整理而得。 1. 按键及传感器模块 1.1 按键简介 按键&#xff1a;常见的输入设备&#xff0c;按下导通&#xff0c;松手断开&#xff1b; 按键抖动&#xff1a;由于按键内部使用的是机械式弹簧片来进行通断的&#xff0c;所以在按下和松手的瞬间…

【ARMv8/v9 GIC 系列 5.8 -- SPI 中断路由到指定的 core 详细介绍】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 SPI 中断路由配置寄存器字段代码示例Usage scenarioSPI 中断路由配置 在ARMv8和ARMv9架构下,当启用亲和性路由(Affinity Routing)时,系统寄存器GICD_IROUTER<n>用于提供具有INTID n的SPI的路由信息。n的最大值由公式(32*…

计算机网络 - 万字长文

计算机网络 二、计算机网络2.1 七层模型表格2.2 通俗讲解七层模型2.3 TCP与UDP对比2.4 TCP 三次握手过程==为什么握手是三次,而不是两次或者四次?====三次握手可以携带数据吗?====TCP三次握手失败,服务端会如何处理?====什么是半连接队列?全连接====ISN(Initial Sequence…

【Python】已解决:FileNotFoundError: [Errno 2] No such file or directory: ‘D:\1. PDF’

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;FileNotFoundError: [Errno 2] No such file or directory: ‘D:\1. PDF’ 一、分析问题背景 在Python编程中&#xff0c;当你尝试打开一个不存在的文件时&…

昇思25天学习打卡营第15天|应用实践之ShuffleNet图像分类

基本介绍 今天的应用实践的领域是计算机视觉领域&#xff0c;更确切的说是图像分类任务&#xff0c;不过&#xff0c;与昨日不同的是&#xff0c;今天所使用的模型是ShuffleNet模型。ShuffleNetV1是旷视科技提出的一种计算高效的CNN模型&#xff0c;和MobileNet, SqueezeNet等一…

26.6 Django模型层

1. 模型层 1.1 模型层的作用 模型层(Model Layer)是MVC或MTV架构中的一个核心组成部分, 它主要负责定义和管理应用程序中的数据结构及其行为. 具体职责包括: * 1. 封装数据: 模型层封装了应用程序所需的所有数据, 这些数据以结构化的形式存在, 如数据库表, 对象等. * 2. 数据…

LabVIEW电滞回线测试系统

铁电材料的性能评估依赖于电滞回线的测量&#xff0c;这直接关系到材料的应用效果和寿命。传统的电滞回线测量方法操作复杂且设备成本高。开发了一种基于LabVIEW的电滞回线测试系统&#xff0c;解决传统方法的不足&#xff0c;降低成本&#xff0c;提高操作便捷性和数据分析的自…