😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你------泰戈尔🍭
⏰发布时间⏰:
本文未经允许,不得转发!!!
目录
- 🎄一、概述
- 🎄二、下载、编译、交叉编译
-
- [✨2.1 faad2 下载](#✨2.1 faad2 下载)
- [✨2.2 faad2 在Ubuntu下编译](#✨2.2 faad2 在Ubuntu下编译)
- [✨2.3 faad2 交叉编译](#✨2.3 faad2 交叉编译)
- [🎄三、faad2 解码库使用流程](#🎄三、faad2 解码库使用流程)
- [🎄四、faad2 解码的例子代码](#🎄四、faad2 解码的例子代码)
-
- [4.1 ✨例子 1](#4.1 ✨例子 1)
- [4.2 ✨例子 2](#4.2 ✨例子 2)
- 🎄五、总结


🎄一、概述
faad
的全称是 Freeware Advanced Audio (AAC) Decoder
,faad2 是一个开源的、免费的 AAC 音频解码器库。它能够将 AAC 编码的音频文件或数据流解码为未压缩的音频数据,如脉冲编码调制(PCM)格式,以便后续的音频播放、编辑等处理。
faad2 支持多种 AAC 音频格式,包括 LC - AAC(Low Complexity AAC)、HE - AAC(High Efficiency AAC)、HE - AAC v2 、MAIN 、 LTP等。这使得它能够处理各种来源的 AAC 音频文件,无论是移动设备上的音频文件,还是在线音频流,都能进行有效的解码。

🎄二、下载、编译、交叉编译
✨2.1 faad2 下载
faad2 的源码在Github托管,下载地址:https://github.com/knik0/faad2/tags。本文下载的是倒数第二个版本:faad2-2.11.1.tar.gz
,见下图。如果GitHub访问不了可以点击这个链接下载:https://download.csdn.net/download/wkd_007/90453816
✨2.2 faad2 在Ubuntu下编译
这个小节介绍 faad2 的源码在 Ubuntu 18.04 系统下编译的步骤:
-
将源码解压缩
shtar zxf faad2-2.11.1.tar.gz
-
进入源码使用cmake编译,这份源码对cmake的版本是有要求的,如果版本太低,需要先升级cmake的版本,不懂的可以参考这篇文章:Ubuntu 18.04 更新 cmake 到最新版本 3.31.2。下面是cmake 编译 faad2 的配置步骤:
shcd faad2-2.11.1/ mkdir build_gcc cd build_gcc/ cmake .. -DCMAKE_C_COMPILER=gcc -DCMAKE_INSTALL_PREFIX::PATH=`pwd`/../../result_gcc -DBUILD_SHARED_LIBS=OFF
DBUILD_SHARED_LIBS=OFF
是为了生成静态库而不是动态库,如果想要动态库,则去掉这个配置。 -
执行make 、make install
shmake make install
编译成功后,会在源码目录的上一级目录生成一个result_gcc
目录,里面就是我们的编译结果。
✨2.3 faad2 交叉编译
这个小节介绍 faad2 的源码在 Ubuntu 18.04 系统下使用 aarch64-mix210-linux-gcc
交叉编译的步骤:
-
将源码解压缩
shtar zxf faad2-2.11.1.tar.gz
-
进入源码使用cmake编译,这份源码对cmake的版本是有要求的,如果版本太低,需要先升级cmake的版本,不懂的可以参考这篇文章:Ubuntu 18.04 更新 cmake 到最新版本 3.31.2。下面是cmake 编译 faad2 的配置步骤:
shcd faad2-2.11.1/ mkdir build_mix210 cd build_mix210/ cmake .. -DCMAKE_C_COMPILER=aarch64-mix210-linux-gcc -DCMAKE_INSTALL_PREFIX::PATH=`pwd`/../../result_mix210 -DBUILD_SHARED_LIBS=OFF
DBUILD_SHARED_LIBS=OFF
是为了生成静态库而不是动态库,如果想要动态库,则去掉这个配置。 -
执行make 、make install
shmake make install
编译成功后,会在源码目录的上一级目录生成一个result_mix210
目录,里面就是我们的编译结果。

🎄三、faad2 解码库使用流程
在 faad2 源码里有一个pdf文件,介绍了 faad2 库的使用,文件路径:faad2-2.11.1_gcc\docs\Ahead AAC Decoder library documentation.pdf
,文件介绍了faad2常用的的API函数和简单使用流程,感兴趣的可以自己去看,下面是我总结的使用流程:
-
1、打开 FAAD 解码器句柄
cppNeAACDecHandle decoder = NeAACDecOpen();;
-
2、配置解码器属性,一般可以省略
cppNeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); // 设置输出格式为 16 位 conf->outputFormat = FAAD_FMT_16BIT; // 启用下混矩阵 conf->downMatrix = 1; // 不使用旧的 ADTS 格式 conf->useOldADTSFormat = 0; // 将配置应用到解码器 if (!NeAACDecSetConfiguration(decoder, conf)) { std::cerr << "Failed to set decoder configuration." << std::endl; return false; }
-
3、初始化解码器,必须先读取一帧ADTS帧作为参数传入,不然通道数、位宽会出错
cppint initRet = NeAACDecInit(decoder, aacframe, size, &samplerate, &channels);
-
4、解码,解码时传入的是一帧完整的ADTS帧
cpppcm_data = (unsigned char*)NeAACDecDecode(decoder, &frame_info, aacframe, size); if(frame_info.error > 0) { printf("%s\n",NeAACDecGetErrorMessage(frame_info.error)); } else if(pcm_data && frame_info.samples > 0) { }
-
5、关闭 FAAD 解码器句柄
cppNeAACDecClose(decoder);

🎄四、faad2 解码的例子代码
4.1 ✨例子 1
下面是根据流程写的例子,保存后,执行 gcc aacDecoder.c -I result_gcc/include/ result_gcc/lib/libfaad.a -lm
编译。
cpp
// gcc aacDecoder.c -I result_gcc/include/ result_gcc/lib/libfaad.a -lm
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "faad.h"
#define FRAME_MAX_LEN 1024*5
#define BUFFER_MAX_LEN 1024*1024
int getADTSframe(unsigned char* buffer, int buf_size, unsigned char* data ,int* data_size){
int size = 0;
if(!buffer || !data || !data_size ){
return -1;
}
while(1){
if(buf_size < 7 ){
return -1;
}
//Sync words
if((buffer[0] == 0xff) && ((buffer[1] & 0xf0) == 0xf0) ){
size |= ((buffer[3] & 0x03) <<11); //high 2 bit
size |= buffer[4]<<3; //middle 8 bit
size |= ((buffer[5] & 0xe0)>>5); //low 3bit
break;
}
--buf_size;
++buffer;
}
if(buf_size < size){
return 1;
}
memcpy(data, buffer, size);
*data_size = size;
return 0;
}
int main(int argc, char* argv[])
{
int data_size = 0;
int size = 0;
int cnt=0;
int offset=0;
unsigned long samplerate=0;
unsigned char channels=0;
NeAACDecHandle decoder = 0;
NeAACDecFrameInfo frame_info;
unsigned char* pcm_data = NULL;
// 判断输入参数 2025-03-03 15:04:01
if(argc < 3)
{
printf("usage\nfaaddec src_file dst_file\n");
return -1;
}
// 获取输入、输出文件 2025-03-03 15:04:22
char src_file[128] = {0};
char dst_file[128] = {0};
sscanf(argv[1], "%s", src_file);
sscanf(argv[2], "%s", dst_file);
FILE *ifile = fopen(src_file, "rb");
FILE *ofile = fopen(dst_file, "wb");
if(!ifile || !ofile)
{
printf("source or destination file");
return -1;
}
//FILE *myout=fopen("output_log.txt","wb+");
FILE *myout=stdout;
unsigned char *aacframe=(unsigned char *)malloc(FRAME_MAX_LEN);
unsigned char *aacbuffer=(unsigned char *)malloc(BUFFER_MAX_LEN);
// 1、打开AAC解码器
decoder = NeAACDecOpen();
// 2、配置解码器属性,一般可以省略 2025-03-03 15:16:23
printf("-----+- ADTS Frame Table -+------+\n");
printf(" NUM | Profile | Frequency| Size |\n");
printf("-----+---------+----------+------+\n");
while(!feof(ifile)){
data_size = fread(aacbuffer+offset, 1, BUFFER_MAX_LEN-offset, ifile);
unsigned char* input_data = aacbuffer;
while(1)
{
int ret=getADTSframe(input_data, data_size, aacframe, &size);
if(ret==-1){
break;
}else if(ret==1){
memcpy(aacbuffer,input_data,data_size);
offset=data_size;
break;
}
char profile_str[10]={0};
char frequence_str[10]={0};
unsigned char profile=aacframe[2]&0xC0;
profile=profile>>6;
switch(profile){
case 0: sprintf(profile_str,"Main");break;
case 1: sprintf(profile_str,"LC");break;
case 2: sprintf(profile_str,"SSR");break;
default:sprintf(profile_str,"unknown");break;
}
unsigned char sampling_frequency_index=aacframe[2]&0x3C;
sampling_frequency_index=sampling_frequency_index>>2;
switch(sampling_frequency_index){
case 0: sprintf(frequence_str,"96000Hz");break;
case 1: sprintf(frequence_str,"88200Hz");break;
case 2: sprintf(frequence_str,"64000Hz");break;
case 3: sprintf(frequence_str,"48000Hz");break;
case 4: sprintf(frequence_str,"44100Hz");break;
case 5: sprintf(frequence_str,"32000Hz");break;
case 6: sprintf(frequence_str,"24000Hz");break;
case 7: sprintf(frequence_str,"22050Hz");break;
case 8: sprintf(frequence_str,"16000Hz");break;
case 9: sprintf(frequence_str,"12000Hz");break;
case 10: sprintf(frequence_str,"11025Hz");break;
case 11: sprintf(frequence_str,"8000Hz");break;
default:sprintf(frequence_str,"unknown");break;
}
fprintf(myout,"%5d| %8s| %8s| %5d|",cnt,profile_str ,frequence_str,size);
// 3、初始化AAC解码器
if(cnt == 0) // 第一个ADTS帧用来初始化解码器 2025-03-03 15:40:53
{
int initRet = NeAACDecInit(decoder, aacframe, size, &samplerate, &channels);
printf("samplerate %ld, channels %d, initRet=%d\n", samplerate, channels, initRet);
}
// 4、解码AAC的 ADTS 帧
pcm_data = (unsigned char*)NeAACDecDecode(decoder, &frame_info, aacframe, size);
if(frame_info.error > 0)
{
printf("%s\n",NeAACDecGetErrorMessage(frame_info.error));
}
else if(pcm_data && frame_info.samples > 0)
{
#if 1
printf("frame info: bytesconsumed %ld, channels %d, header_type %d object_type %d, samples %ld, samplerate %ld\n",
frame_info.bytesconsumed,
frame_info.channels, frame_info.header_type,
frame_info.object_type, frame_info.samples,
frame_info.samplerate);
#endif
fwrite(pcm_data, 1, frame_info.samples * frame_info.channels, ofile); //2个通道
fflush(ofile);
}
data_size -= size;
input_data += size;
cnt++;
}
}
NeAACDecClose(decoder); // 5、关闭解码器
fclose(ifile);
fclose(ofile);
free(aacbuffer);
free(aacframe);
return 0;
}
4.2 ✨例子 2
下面是让 AI 写的代码:使用 C++ 和 FAAD 库将使用 ADTS 帧的 AAC 编码文件分别解码为 WAV 编码文件和 PCM 文件的示例代码,将调用 FAAD 的部分封装成了一个类,并且添加了详细的注释。
cpp
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <faad.h>
// 定义 AAC 解码器类
class AACDecoder {
private:
NeAACDecHandle decoder; // FAAD 解码器句柄
unsigned long samplerate; // 音频采样率
unsigned char channels; // 音频通道数
// 从文件中读取一个 ADTS 帧
bool readADTSFrame(std::ifstream& inputFile, unsigned char* buffer, size_t bufferSize, size_t& frameSize) {
if (inputFile.eof()) return false;
// 查找 ADTS 帧头
while (inputFile.read(reinterpret_cast<char*>(buffer), 2)) {
if ((buffer[0] == 0xFF) && ((buffer[1] & 0xF0) == 0xF0)) {
// 找到帧头,继续读取剩余的帧头部分
inputFile.read(reinterpret_cast<char*>(buffer + 2), 5);
// 从帧头中提取帧大小
frameSize = ((buffer[3] & 0x03) << 11) | (buffer[4] << 3) | ((buffer[5] & 0xE0) >> 5);
if (frameSize > bufferSize) {
std::cerr << "ADTS frame size exceeds buffer size." << std::endl;
return false;
}
// 读取剩余的帧数据
inputFile.read(reinterpret_cast<char*>(buffer + 7), frameSize - 7);
return true;
}
}
return false;
}
public:
// 构造函数,初始化解码器
AACDecoder() : decoder(nullptr), samplerate(0), channels(0) {
decoder = NeAACDecOpen();
if (decoder == nullptr) {
std::cerr << "Failed to open FAAD decoder." << std::endl;
}
}
// 析构函数,关闭解码器
~AACDecoder() {
if (decoder != nullptr) {
NeAACDecClose(decoder);
}
}
// 配置解码器
bool configureDecoder() {
// 获取当前解码器配置
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder);
if (conf == nullptr) {
std::cerr << "Failed to get decoder configuration." << std::endl;
return false;
}
// 设置输出格式为 16 位
conf->outputFormat = FAAD_FMT_16BIT;
// 启用下混矩阵
conf->downMatrix = 1;
// 不使用旧的 ADTS 格式
conf->useOldADTSFormat = 0;
// 将配置应用到解码器
if (!NeAACDecSetConfiguration(decoder, conf)) {
std::cerr << "Failed to set decoder configuration." << std::endl;
return false;
}
return true;
}
// 解码 AAC 文件并保存为 WAV 文件
bool decodeAACToWAV(const std::string& inputFileName, const std::string& outputFileName) {
// 打开输入的 AAC 文件
std::ifstream inputFile(inputFileName, std::ios::binary);
if (!inputFile) {
std::cerr << "Failed to open input file." << std::endl;
return false;
}
// 配置解码器
if (!configureDecoder()) {
inputFile.close();
return false;
}
// 打开输出的 WAV 文件
std::ofstream outputFile(outputFileName, std::ios::binary);
if (!outputFile) {
std::cerr << "Failed to open output file." << std::endl;
inputFile.close();
return false;
}
unsigned char buffer[8192];
size_t frameSize;
bool isFirstFrame = true;
std::streamsize totalDecodedSamples = 0;
// 读取第一个 ADTS 帧用于初始化解码器
if (!readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
std::cerr << "Failed to read the first ADTS frame for initialization." << std::endl;
inputFile.close();
outputFile.close();
return false;
}
// 初始化解码器
if (NeAACDecInit(decoder, buffer, static_cast<unsigned long>(frameSize), &samplerate, &channels) < 0) {
std::cerr << "Failed to initialize FAAD decoder." << std::endl;
inputFile.close();
outputFile.close();
return false;
}
// 写入 WAV 文件头
writeWAVHeader(outputFile, samplerate, channels);
// 处理第一个帧的解码
NeAACDecFrameInfo frameInfo;
void* decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));
if (frameInfo.error > 0) {
std::cerr << "Decoding error in the first frame: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
} else if (frameInfo.samples > 0) {
outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
totalDecodedSamples += frameInfo.samples;
}
// 循环读取并解码剩余的 ADTS 帧
while (readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));
if (frameInfo.error > 0) {
// 输出解码错误信息
std::cerr << "Decoding error: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
continue;
}
if (frameInfo.samples > 0) {
// 将解码后的数据写入 WAV 文件
outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
totalDecodedSamples += frameInfo.samples;
}
}
// 更新 WAV 文件头中的文件大小和数据大小
updateWAVHeader(outputFile, totalDecodedSamples, samplerate, channels);
// 关闭输入和输出文件
inputFile.close();
outputFile.close();
return true;
}
// 解码 AAC 文件并保存为 PCM 文件
bool decodeAACToPCM(const std::string& inputFileName, const std::string& outputFileName) {
// 打开输入的 AAC 文件
std::ifstream inputFile(inputFileName, std::ios::binary);
if (!inputFile) {
std::cerr << "Failed to open input file." << std::endl;
return false;
}
// 配置解码器
if (!configureDecoder()) {
inputFile.close();
return false;
}
// 打开输出的 PCM 文件
std::ofstream outputFile(outputFileName, std::ios::binary);
if (!outputFile) {
std::cerr << "Failed to open output file." << std::endl;
inputFile.close();
return false;
}
unsigned char buffer[8192];
size_t frameSize;
// 读取第一个 ADTS 帧用于初始化解码器
if (!readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
std::cerr << "Failed to read the first ADTS frame for initialization." << std::endl;
inputFile.close();
outputFile.close();
return false;
}
// 初始化解码器
if (NeAACDecInit(decoder, buffer, static_cast<unsigned long>(frameSize), &samplerate, &channels) < 0) {
std::cerr << "Failed to initialize FAAD decoder." << std::endl;
inputFile.close();
outputFile.close();
return false;
}
// 处理第一个帧的解码
NeAACDecFrameInfo frameInfo;
void* decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));
if (frameInfo.error > 0) {
std::cerr << "Decoding error in the first frame: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
} else if (frameInfo.samples > 0) {
outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
}
// 循环读取并解码剩余的 ADTS 帧
while (readADTSFrame(inputFile, buffer, sizeof(buffer), frameSize)) {
decodedData = NeAACDecDecode(decoder, &frameInfo, buffer, static_cast<unsigned long>(frameSize));
if (frameInfo.error > 0) {
// 输出解码错误信息
std::cerr << "Decoding error: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
continue;
}
if (frameInfo.samples > 0) {
// 将解码后的数据写入 PCM 文件
outputFile.write(reinterpret_cast<const char*>(decodedData), frameInfo.samples * sizeof(short));
}
}
// 关闭输入和输出文件
inputFile.close();
outputFile.close();
return true;
}
// 写入 WAV 文件头
void writeWAVHeader(std::ofstream& outputFile, unsigned long samplerate, unsigned char channels) {
// 采样位数
const int bitsPerSample = 16;
// 块对齐
const int blockAlign = channels * (bitsPerSample / 8);
// 每秒数据字节数
const int byteRate = samplerate * blockAlign;
// 写入 RIFF 头
outputFile.write("RIFF", 4);
// 这里先占位,后续再更新文件大小
unsigned int fileSizePlaceholder = 0;
outputFile.write(reinterpret_cast<const char*>(&fileSizePlaceholder), 4);
outputFile.write("WAVE", 4);
// 写入 fmt 块
outputFile.write("fmt ", 4);
unsigned int fmtChunkSize = 16;
outputFile.write(reinterpret_cast<const char*>(&fmtChunkSize), 4);
unsigned short audioFormat = 1; // PCM 格式
outputFile.write(reinterpret_cast<const char*>(&audioFormat), 2);
outputFile.write(reinterpret_cast<const char*>(&channels), 2);
outputFile.write(reinterpret_cast<const char*>(&samplerate), 4);
outputFile.write(reinterpret_cast<const char*>(&byteRate), 4);
outputFile.write(reinterpret_cast<const char*>(&blockAlign), 2);
outputFile.write(reinterpret_cast<const char*>(&bitsPerSample), 2);
// 写入 data 块
outputFile.write("data", 4);
// 这里先占位,后续再更新数据大小
unsigned int dataSizePlaceholder = 0;
outputFile.write(reinterpret_cast<const char*>(&dataSizePlaceholder), 4);
}
// 更新 WAV 文件头中的文件大小和数据大小
void updateWAVHeader(std::ofstream& outputFile, std::streamsize totalDecodedSamples, unsigned long samplerate, unsigned char channels) {
const int bitsPerSample = 16;
const int blockAlign = channels * (bitsPerSample / 8);
unsigned int dataSize = static_cast<unsigned int>(totalDecodedSamples * sizeof(short));
unsigned int fileSize = dataSize + 36;
// 回到文件开头更新文件大小
outputFile.seekp(4, std::ios::beg);
outputFile.write(reinterpret_cast<const char*>(&fileSize), 4);
// 回到 data 块更新数据大小
outputFile.seekp(40, std::ios::beg);
outputFile.write(reinterpret_cast<const char*>(&dataSize), 4);
}
};
int main() {
AACDecoder decoder;
std::string inputFileName = "input.aac";
std::string wavOutputFileName = "output.wav";
std::string pcmOutputFileName = "output.pcm";
if (decoder.decodeAACToWAV(inputFileName, wavOutputFileName)) {
std::cout << "Decoded AAC to WAV successfully." << std::endl;
} else {
std::cout << "Failed to decode AAC to WAV." << std::endl;
}
if (decoder.decodeAACToPCM(inputFileName, pcmOutputFileName)) {
std::cout << "Decoded AAC to PCM successfully." << std::endl;
} else {
std::cout << "Failed to decode AAC to PCM." << std::endl;
}
return 0;
}

🎄五、总结
本文介绍了AAC音频解码库 faad2 ,并介绍了其使用流程,最后给出了使用例子源码

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
参考:
https://blog.csdn.net/qq_43603125/article/details/138583930
https://blog.csdn.net/weixin_45266730/article/details/140501073