目录
3、AACENC_ERROR aacEncInfo(const HANDLE_AACENCODER hAacEncoder,AACENC_InfoStruct *pInfo);
5、AACENC_ERROR aacEncClose( HANDLE_AACENCODER *phAacEncoder);
《libfdk-aac实现PCM编码AAC代码实现》链接:
https://edu.csdn.net/learn/38258/606136?spm=1003.2001.3001.4157
前言
1、AAC介绍
AAC(高级音频编码) 是一种用于有损数字音频压缩的音频编码标准。作为MP3格式的继承者,AAC 在相同比特率下通常可以实现比 MP3 编码器更高的音质;早在1997 年AAC 已被ISO和IEC 标准化为MPEG-2和MPEG-4规范的一部分。AAC 提供 8 kHz 到 96 kHz 之间的采样频率和 1 到 48 之间的任意数量的通道;默认情况下,AAC编码将使用更长的 1024 点/960 点进行压缩编码。AAC属于MPEG-2和MPEG-4规范的一部分,具有复杂的规格,共分为9种规格/档次,如下:
MPEG-2 AAC:
1、MPEG-2 AAC LC低复杂度规格(Low Complexity);
2、MPEG-2 AAC Main主规格;
3、MPEG-2 AAC SSR可变取样率规格(Scaleable Sample Rate);
MPEG-4 AAC:
4、MPEG-4 AAC LC低复杂度规格(Low Complexity),现在的手机比较常见的MP4格式中的音频部份就包括了该种规格的音频;
5、MPEG-4 AAC Main主规格;
6、MPEG-4 AAC SSR可变取样率规格(Scaleable Sample Rate);
7、MPEG-4 AAC LTP长时期预测规格(Long Term Predicition);
8、MPEG-4 AAC LD低延迟规格(Low Delay);
9、MPEG-4 AAC HE高效率规格(High Efficiency)。
2、AAC 格式
MPEG-2 中定义了AAC的两种格式分别用于文件存储的ADIF(音频数据交换格式),以及用于传输流中传输数据的ADTS(音频数据传输流)格式。
ADIF格式的AAC音频流由ADIF头数据和AAC音频流组成,通常一个文件中只有一个ADIF头,也就是说ADIF格式的AAC解码必须从文件头部第一帧开始解码。如下图所示在AAC 编码数据流之前有一个ADIF的头信息。
在AAC标准文档中对ADIF码流格式的介绍如下图1,一个ADIF的音频序列,中首先是ADIF的头,和对齐字节,之后是编码的原始数据流。下图2是ADIF Header的的定义。
图1
图2
在ADTS格式的音频流中每个音频帧头部有包含一个ADTS的头;和ADIF不同ADTS格式的音频可以从任意一帧AAC音频数据进行解码,无需从音频一帧进行解码。ADTS格式的音频数据流如下图所示。
在AAC标准文档中对ADTS码流格式的介绍如下图3;ADTS格式的音频数据通过SyncWord来进行判断下一帧音频。
图3
如下图4,是ADTS格式一帧音频的格式。一帧音频包含了ADTS的固定头adts_fixed_header和可变头adts_variable_header,adts错误检查字段,以及aac编码后的原始数据raw_data_block。
图4
下图5是ADTS的头信息定义,包含了固定头(Fixed Header)和可变头(Variable Header)。可以看出在ADTS的固定头中包含了AAC编码的档次、通道数、采样率索引等。在可变头中包含了帧长度等信息。ADTS的头信息共56Bit(7个字节),在RTP传输ADTS格式的音频时候通常去掉7个字节的ADTS头数据,在解码端重新组装ADTS的头。
图5
一、libfdk_aac介绍
libfdk_aac是一个开源库,用于以AAC 格式对数字音频数据进行编码和解码。它支持多种音频对象类型,包括MPEG-2和MPEG-4 AAC LC、HE-AAC (AAC LC + SBR )、HE-AACv2 (LC + SBR + PS ) 以及AAC-LD (low延迟)和 AAC-ELD(增强低延迟)。编码库支持采样率8~ 96 kHz ,最多可以支持8 个通道(7.1 环绕声)。fdk_aac 支持的音频对象如下表1。
表1
AAC Profile名称 | MPEG-4 Audio object type | FDK_AAC是否支持 |
低复杂度 (AAC-LC) | 2 | YES |
高效 (HE-AAC) | 2、5 | YES |
高效版本 2 (HE-AAC) | 2、5、29 | YES |
低延迟 (AAC-LD) | 23 | YES |
增强型低延迟 (AAC-ELD) | 39 | YES |
fdk_aac 支持的音频采样率下表2。
表2
采样率 | 8000 | 11025 | 12000 | 16000 | 22050 | 24000 | 32000 | 44100 | 48000 | 64000 | 88200 | 96000 |
libfdk_aac源码下载路径为github:https://github.com/mstorsjo/fdk-aac.git。
二、libfdk_aac主要API介绍
1、AACENC_ERROR aacEncOpen(HANDLE_AACENCODER *phAacEncoder,const UINT encModules,const UINT maxChannels);
该函数用于打开AAC编码器,成功返回0;函数共有3个参数;phAacEncoder:成功打开编码器后返回AAC编码器的句柄;encModules:通常默认0,表示编码器内部分配内存maxChannels:音频通道数,范围1~8。
2、AACENC_ERROR aacEncoder_SetParam(const HANDLE_AACENCODER hAacEncoder,const AACENC_PARAM param,const UINT value);
该函数用于设置AAC编码器参数。成功返回0;函数共有3个参数;phAacEncoder:编码句柄;param:编码器参数类型详细见表3;value:参数值。param常见的取值如下表3所示,详细类型定义在AACENC_PARAM中;主要包含音频对象规格、码率、采样率、帧长度、声道模式、编码帧类型等。
表3
AAC 参数类型 | 参数说明 | 参数取值范围 |
AACENC_AOT | 音频对象类型 | 2: MPEG-4 AAC Low Complexity等详细见表1 |
AACENC_BITRATE | 编码码率 | 如64k,96k等 |
AACENC_BITRATEMODE | 码率模式 | 0:CBR固定码率,1-7其他模式 |
AACENC_SAMPLERATE | 音频采样率 | 48000,44100等,详细见表2 |
AACENC_SBR_MODE | 音普带复制 | 在ELD( Enhanced Low-Delay.)下参数配置 |
AACENC_GRANULE_LENGTH | 编码帧长度 | 1024 512 480 |
AACENC_CHANNELMODE | 声道模式 | 单声道、双声道、8声道等 |
AACENC_CHANNELORDER | 输入音频声道排列方式 | 0:采用MPEG声道排列方式。1:采用wav声道排列方式 |
AACENC_AFTERBURNER | 通过消耗内存和CPU来提升音质 | 0:关闭,1:开启 |
AACENC_BANDWIDTH | 编码带宽 | 0:编码器内部决定带宽,其他值表示配置等目标带宽 |
AACENC_TRANSMUX | 编码帧类型 | 0:原始编码帧,1:带有ADIF头的编码帧,2:带有ADTS头的编码帧,其他:6/7/10 |
3、AACENC_ERROR aacEncInfo(const HANDLE_AACENCODER hAacEncoder,AACENC_InfoStruct *pInfo);
该函数用于获取编码器信息。函数有2个参数;hAacEncoder编码器句柄;pInfo:编码器信息。AACENC_InfoStruct的定义如下;主要包含声道数、帧长度、编码延长等。
typedef struct {
UINT maxOutBufBytes; /*!< Maximum number of encoder bitstream bytes within one frame.
Size depends on maximum number of supported channels in encoder instance.
For superframing (as used for example in DAB+), size has to be a multiple accordingly. */
UINT maxAncBytes; /*!< Maximum number of ancillary data bytes which can be inserted into
bitstream within one frame. */
UINT inBufFillLevel; /*!< Internal input buffer fill level in samples per channel. This parameter
will automatically be cleared if samplingrate or channel(Mode/Order) changes. */
UINT inputChannels; /*!< Number of input channels expected in encoding process. */
UINT frameLength; /*!< Amount of input audio samples consumed each frame per channel, depending
on audio object type configuration. */
UINT encoderDelay; /*!< Codec delay in PCM samples/channel. Depends on framelength and AOT. Does not
include framing delay for filling up encoder PCM input buffer. */
UCHAR confBuf[64]; /*!< Configuration buffer in binary format as an AudioSpecificConfig
or StreamMuxConfig according to the selected transport type. */
UINT confSize; /*!< Number of valid bytes in confBuf. */
} AACENC_InfoStruct;
4、AACENC_ERROR aacEncEncode(const HANDLE_AACENCODER hAacEncoder,const AACENC_BufDesc *inBufDesc,const AACENC_BufDesc *outBufDesc,const AACENC_InArgs *inargs,AACENC_OutArgs *outargs);
该函数用于编码一帧音频数据并输出编码后的音频帧。该函数有5个参数;hAacEncoder:编码器句柄;inBufDesc:输入原始PCM音频数据;outBufDesc:输出编码后的AAC音频数据;inargs:输入原始PCM音频参数,如输入音频所有通道的采样点数;outargs:输出编码后AAC音频参数,如输出AAC帧长度等。
5、AACENC_ERROR aacEncClose( HANDLE_AACENCODER *phAacEncoder);
该函数用于关闭AAC编码器。
三、libfdk_aac编码示例
1、打开编码器,初始化编码器参数
static HANDLE_AACENCODER init_fdk_aac(int sample_rate,int channels,int bitrate)
{
HANDLE_AACENCODER handle;
CHANNEL_MODE mode;
int aot = 2; //lc 低复杂度,算法速度快
switch (channels) {
case 1: mode = MODE_1; break;
case 2: mode = MODE_2; break;
case 3: mode = MODE_1_2; break;
case 4: mode = MODE_1_2_1; break;
case 5: mode = MODE_1_2_2; break;
case 6: mode = MODE_1_2_2_1; break;
default:
fprintf(stderr, "Unsupported WAV channels %d\n", channels);
return NULL;
}
//打开aac 编码器
if (aacEncOpen(&handle, 0, channels) != AACENC_OK) {
fprintf(stderr, "Unable to open encoder\n");
return NULL;
}
//设置算法模式 lc等
if (aacEncoder_SetParam(handle, AACENC_AOT, aot) != AACENC_OK) {
fprintf(stderr, "Unable to set the AOT\n");
return NULL;
}
//设置编码输入帧长
if (aacEncoder_SetParam(handle, AACENC_GRANULE_LENGTH, 1024) != AACENC_OK) {
fprintf(stderr, "Unable to set the audio frame length\n");
return NULL;
}
//设置采样率
if (aacEncoder_SetParam(handle, AACENC_SAMPLERATE, sample_rate) != AACENC_OK) {
fprintf(stderr, "Unable to set the AOT\n");
return NULL;
}
//声道模式
if (aacEncoder_SetParam(handle, AACENC_CHANNELMODE, mode) != AACENC_OK) {
fprintf(stderr, "Unable to set the channel mode\n");
return NULL;
}
//设置pcm数据格式
if (aacEncoder_SetParam(handle, AACENC_CHANNELORDER, 1) != AACENC_OK) {
fprintf(stderr, "Unable to set the wav channel order\n");
return NULL;
}
//设置编码码率
if (aacEncoder_SetParam(handle, AACENC_BITRATE, bitrate) != AACENC_OK) {
fprintf(stderr, "Unable to set the bitrate\n");
return NULL;
}
//设置编码帧为ADTS格式
if (aacEncoder_SetParam(handle, AACENC_TRANSMUX, TT_MP4_ADTS) != AACENC_OK) {
fprintf(stderr, "Unable to set the ADTS transmux\n");
return NULL;
}
//初始化 handle
if (aacEncEncode(handle, NULL, NULL, NULL, NULL) != AACENC_OK) {
fprintf(stderr, "Unable to initialize the encoder\n");
return NULL;
}
//获取音频编码信息
if (aacEncInfo(handle, &g_aacInfo) != AACENC_OK) {
fprintf(stderr, "Unable to get the encoder info\n");
return NULL;
}
return handle;
}
2、关闭编码器
static void deInit_fdk_aac(HANDLE_AACENCODER *pHandle)
{
aacEncClose(pHandle);
}
3、编码PCM音频帧为AAC
static int fdk_aac_enc(HANDLE_AACENCODER *pHandle,uint16_t *pInPcmData,int inputSize,uint8_t *pOutBuff,int outBuffsize)
{
AACENC_ERROR err;
AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 };
AACENC_InArgs in_args = { 0 };
AACENC_OutArgs out_args = { 0 };
int in_identifier = IN_AUDIO_DATA;
int out_identifier = OUT_BITSTREAM_DATA;
int in_size = inputSize, in_elem_size = 2;
while(1)
{
void *in_ptr = pInPcmData;
//输入参数和输入buff配置
in_args.numInSamples = inputSize <= 0 ? -1 : inputSize/in_elem_size;//输入数据中所有声道采样点个数
in_buf.numBufs = 1; //输入buff个数
in_buf.bufs = &in_ptr; //输入PCM音频地址
in_buf.bufferIdentifiers = &in_identifier;//输入
in_buf.bufSizes = &in_size; //输入PCM大小
in_buf.bufElSizes = &in_elem_size;//每个采样点数据类型(大小)short
//输出参数和输出buff配置
void * out_ptr = pOutBuff;
int out_size = outBuffsize;
int out_elem_size = 1; //输出每个
out_buf.numBufs = 1; //输出buff个数
out_buf.bufs = &out_ptr;//输出buff的地址
out_buf.bufferIdentifiers = &out_identifier;
out_buf.bufSizes = &out_size;//输出buff的大小
out_buf.bufElSizes = &out_elem_size;//输出数据类型(大小)char
if ((err = aacEncEncode(*pHandle, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK) {
if (err == AACENC_ENCODE_EOF)
break;
fprintf(stderr, "Encoding failed\n");
return 0;
}
if (out_args.numOutBytes == 0)
continue;
break;
}
return out_args.numOutBytes;
}
四、AAC编码器软件框架
AAC编码代码实现请参考 <音频数据进行AAC编码课程>