音频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对输入的编码数据格式做了些说明:网页链接
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 #include #include #include #include #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 break; } /* AAC编码 5/8:填充编码器需要的参数,包括编码的pcm数据地址,大小等 */ inPcmBufDesc.numBufs = 1; inPcmBufDesc.bufs = (void **)&pu8PcmInBuf; inPcmBufDesc.bufferIdentifiers = &inIdentifier; inPcmBufDesc.bufSizes = &s32ReadPcmBytes; inPcmBufDesc.bufElSizes = &inElsize; inArgs.numInSamples = (s32ReadPcmBytes 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
-
-
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

