解码aac编码格式音频转pcm(使用faad2库)
阅读本博客之前,可选阅读《音频相关基础知识》:https://blog.csdn.net/qq_41824928/article/details/108124382
1.使用faad2库
使用faad2库解码aac为pcm时,主要使用以下几个函数:
NeAACDecHandle NeAACDecOpen(void);
void NeAACDecClose(NeAACDecHandle hDecoder);
NeAACDecConfigurationPtr NeAACDecGetCurrentConfiguration(NeAACDecHandle hDecoder);
unsigned char NeAACDecSetConfiguration(NeAACDecHandle hDecoder, NeAACDecConfigurationPtr config);
long NeAACDecInit(NeAACDecHandle hDecoder, unsigned char *buffer, unsigned long buffer_size, unsigned long *samplerate, unsigned char *channels);
void* NeAACDecDecode(NeAACDecHandle hDecoder, NeAACDecFrameInfo *hInfo, unsigned char *buffer, unsigned long buffer_size);
我们从上往下进行介绍:
1.1 打开和关闭handle
typedef void *NeAACDecHandle;
创建一个用于aac解码的handle, NeAACDecHandle类型是void*, 所以在使用结束后,需要关闭这个句柄释放资源。
NeAACDecHandle NeAACDecOpen(void);
关闭一个用于aac解码的handle
void NeAACDecClose(NeAACDecHandle hDecoder);
1.2获取和设置解码参数
typedef struct NeAACDecConfiguration
{
unsigned char defObjectType;
unsigned long defSampleRate;
unsigned char outputFormat;
unsigned char downMatrix;
unsigned char useOldADTSFormat;
unsigned char dontUpSampleImplicitSBR;
} NeAACDecConfiguration, *NeAACDecConfigurationPtr;
获取当前的解码参数
NeAACDecConfigurationPtr NeAACDecGetCurrentConfiguration(NeAACDecHandle hDecoder);
设置解码参数
unsigned char NeAACDecSetConfiguration(NeAACDecHandle hDecoder, NeAACDecConfigurationPtr config);
设置解码参数时,主要设置以下4个参数defSampleRate,defObjectType,outputFormat,dontUpSampleImplicitSBR(其实defSampleRate 和 defObjectType随意设置即可)
outputFormat: 位深度的设置。位深度概念可看整理转载的博客《音频相关基础知识》: https://blog.csdn.net/qq_41824928/article/details/108124382
dontUpSampleImplicitSBR设置成1,可以禁止NeAACDecInit时,采样率(小于等于24000时)翻倍的问题,NeAACDecInit涉及这一块的源码部分如下:
if (*samplerate <= 24000 && (hDecoder->config.dontUpSampleImplicitSBR == 0))
{
*samplerate *= 2;
hDecoder->forceUpSampling = 1;
} else if (*samplerate > 24000 && (hDecoder->config.dontUpSampleImplicitSBR == 0)) {
hDecoder->downSampledSBR = 1;
}
1.3初始化解码器
/*
* @brief 用aac的adts/adif头初始化解码器。只用初始化一次!
* @param hDecoder[in] NeAACDecOpen生成的handle
* buffer[in] aac数据的起始地址
* buffer_size[in] aac数据长度
* samplerate[out] 采样率
* channels[out] 通道数
* @return 失败返回-1
*/
long NeAACDecInit(NeAACDecHandle hDecoder,
unsigned char *buffer,
unsigned long buffer_size,
unsigned long *samplerate,
unsigned char *channels);
1.4解码aac为pcm
typedef struct NeAACDecFrameInfo
{
unsigned long bytesconsumed;
unsigned long samples;
unsigned char channels;
unsigned char error;
unsigned long samplerate;
/* SBR: 0: off, 1: on; upsample, 2: on; downsampled, 3: off; upsampled */
unsigned char sbr;
/* MPEG-4 ObjectType */
unsigned char object_type;
/* AAC header type; MP4 will be signalled as RAW also */
unsigned char header_type;
/* multichannel configuration */
unsigned char num_front_channels;
unsigned char num_side_channels;
unsigned char num_back_channels;
unsigned char num_lfe_channels;
unsigned char channel_position[64];
/* PS: 0: off, 1: on */
unsigned char ps;
} NeAACDecFrameInfo;
/*
* @brief aac解码为pcm
* @param hDecoder[in] NeAACDecOpen生成的handle
* hInfo[out] 解码获取到的参数信息
* buffer[in] aac数据的起始地址
* buffer_size[in] aac数据长度
* @return 成功返回pcm的起始地址(长度需要自己计算),失败返回NULL,并且可以从hInfo->error中获得错误码
*/
void* NeAACDecDecode(NeAACDecHandle hDecoder,
NeAACDecFrameInfo *hInfo,
unsigned char *buffer,
unsigned long buffer_size);
NeAACDecDecode解码成功得到pcm的起始地址的指针,但是长度没有给出,需要我们自己进行计算,计算解码后pcm的长度可看整理转载的博客《音频相关基础知识》: https://blog.csdn.net/qq_41824928/article/details/108124382 最下方红色加粗的部分。
2.切分AAC帧数据
因为faad2库的解码函数NeAACDecDecode,需要的参数是aac的帧数据,不满一帧会解码失败,超过一帧不满两帧,只会解码一帧的数据,后面的数据就会被丢弃掉。
因此,在使用NeAACDecDecode之前,我们需要把aac音频做切割,分裂成一帧一帧的数据。aac(adts头)是以syncword作为起始码的(值一定是0xfff),layer固定为('00'),我们可以通过syncword来进行分割aac数据。
如果你使用的是srs_librtmp拉aac音频流,则可以跳过这里。因为在调用srs_rtmp_read_packet时获取到的音频数据,都是完整的一帧。
aac的adts头详解,以及srs_librtmp拉aac音频流,可以看博客《RTMP拉流保存aac(flv保存为aac)》: https://blog.csdn.net/qq_41824928/article/details/107636845
adts_fixed_header前2个字节的结构如图:
- syncword:帧同步标识一个帧的开始,固定为0xFFF
- ID:MPEG 标示符。0表示MPEG-4,1表示MPEG-2
- layer:固定为'00'
- p_a (protection_absent):标识是否进行误码校验。0表示有CRC校验,1表示没有CRC校验,CRC校验总共2个字节(16bit)
adts_header分了2部分,每部分是28bit,其中表示aac长度的aac_frame_length在第2部分,是13bit,因此我们可以根据syncword, layer来查找aac的起始位置,并通过aac_frame_length来得到aac帧的长度,代码如下:
//aac开头的第一个字节必须是0xff
std::string::size_type pos = aacData.find(0xff, 0);
if (pos == std::string::npos) {
return "";
}
//aac开头的第二个字节的前4位必须是f, 6、7位必须是0
uint8_t code = aacData[pos+1];
if (((code & (uint8_t)0xf0) != 0xf0) || (code & (uint8_t)0x06) != 0) {
return "";
}
//解析得到aac帧长度
uint8_t i = aacData[pos+3];
uint8_t j = aacData[pos+4];
uint8_t k = aacData[pos+5];
uint32_t aacFrameLen = ((i & 0x3) << 9) + (j << 3) + ((k & 0xe0) >> 5);
return aacData.substr(pos, aacFrameLen);
3.总结
1. NeAACDecOpen打开一个句柄,通过NeAACDecGetCurrentConfiguration获取解码参数结构体,通过NeAACDecSetConfiguration设置解码参数。
2. 获取aac帧数据(可根据adts_header进行解析,也可以通过srs_librtmp直接拉取rtmp流获取帧数据)。
3. 调用NeAACDecInit,用aac帧数据初始化句柄NeAACDecHandle。
4. 调用NeAACDecDecode解码aac帧数据为pcm数据。
5. 使用ffplay播放pcm
(例如播放采样率44100HZ,立体声(2通道的),16位深的pcm:ffplay -ar 44100 -channels 2 -f s16le -i xxx.pcm)