2020-03-06 18:57:04 mo4776 阅读数 0

背景

前面几篇文章介绍了AAC的几种封装标准及libfdk-aac的一些使用细节,这篇文章就给出libfdk-aac编解码代码示例。

示例

编码

编码封装类CAACEncoder适用于RTP流媒体应用,设置的AAC编码规格为AAC-LD,封装格式为LATM,设置编码输入的采样点数为480(适用于采样率为48000),包复用个数为2(适用于采样周期为20ms)

编码接口 int EncodeFrame(unsigned char* pExpandedFrame,unsigned int len, unsigned char* pEncodedFrame)len表示raw audio frame的长度,应该为480的整数倍

头文件

#ifndef FDK_AAC_ENCODER_H
#define FDK_AAC_ENCODER_H
#include <mutex>

#include "libAACenc/include/aacenc_lib.h"

#define AAC_MAX_SAMPLE_CNT 480

class CAACEncoder
{
public:
	CAACEncoder();
	~CAACEncoder();
	int Init();
	int Start();
	int Stop();

	void SetCodecParameters(
		int ChannelNum,
		int AudioSampleRate,
		int AudioBitrate,
		int EncodeMode,
		int FrameSize);
	int EncodeFrame(unsigned char* pExpandedFrame,
		unsigned int len,
		unsigned char* pEncodedFrame);

	int GetEncodedFrameSize()
	{
		int tmp = m_EncodedFrameSize;
		m_EncodedFrameSize = 0;
		return tmp;
	};

private:
	void Destroy();
private:

	int m_EncodedFrameSize;

	//fdk-aac
	HANDLE_AACENCODER m_AAChandle;
	CHANNEL_MODE m_ChannelMode;

	unsigned int m_iEncoderBufferSize;
	//2->AAC-LC 5->HE-AAC 29->HE-AAC-v2 23->AAC-LD 39->AAC-ELD
	int m_iEncodMode;
	int m_iAudioSample;
	int m_iAudioBitrate;
};

#endif

cpp文件

#include "AACEncoder.h"

CAACEncoder::CAACEncoder() :
	m_AAChandle(NULL)
	, m_ChannelMode(MODE_UNKNOWN)
	, m_iEncodMode(2)
	, m_iEncoderBufferSize(0)
	, m_iAudioSample(0)
	, m_iAudioBitrate(0)
	, m_EncodedFrameSize(0)
{
}

CAACEncoder::~CAACEncoder()
{
	Destroy();
}


void CAACEncoder::SetCodecParameters(int ChannelNum,
	int AudioSampleRate,
	int AudioBitrate,
	int EncodeMode,
	int FrameSize)
{

	//音频声道数
	if (1 == ChannelNum)
	{//单声道
		m_ChannelMode = MODE_1;
	}
	else if (2 == ChannelNum)
	{//双声道
		m_ChannelMode = MODE_2;
	}


	m_iEncodMode = EncodeMode;
	m_iAudioSample = AudioSampleRate;
	m_iAudioBitrate = AudioBitrate;
}

int CAACEncoder::EncodeFrame(unsigned char* pExpandedFrame,
	unsigned int len,
	unsigned char* pEncodedFrame)
{
	unsigned char* pOutData = pEncodedFrame;
	unsigned int iAlreadyConsumeBytes = 0;
	unsigned char* pData = pExpandedFrame;
	while (1)
	{
		void* pInputData[] = { (void*)(pData + iAlreadyConsumeBytes) };
		void *pOutputData[] = { (void*)(pEncodedFrame + m_EncodedFrameSize) };

		int inSize = (int)len;
		//一个采样点占两个字节
		int iSampleBytes = 2;
		AACENC_BufDesc inBuf = { 0 };
		AACENC_InArgs inArgs = { 0 };
		//输入缓存的标识
		int in_identifier = IN_AUDIO_DATA;
		AACENC_ERROR err;

		//采样点个数
		inArgs.numInSamples = (len <= 0 ? -1 : inSize / 2);
		inBuf.numBufs = 1;
		inBuf.bufs = pInputData;
		inBuf.bufferIdentifiers = &in_identifier;
		inBuf.bufSizes = &inSize;
		inBuf.bufElSizes = &iSampleBytes;

		AACENC_BufDesc outBuf = { 0 };
		AACENC_OutArgs outArgs = { 0 };
		//输出缓存的标识
		int out_identifier = OUT_BITSTREAM_DATA;
		int outElemSize = 1;
		outBuf.numBufs = 1;
		outBuf.bufs = pOutputData;
		outBuf.bufferIdentifiers = &out_identifier;
		int OutBufSizes = m_iEncoderBufferSize;
		outBuf.bufSizes = &OutBufSizes;
		outBuf.bufElSizes = &outElemSize;

		if ((err = aacEncEncode(m_AAChandle, &inBuf, &outBuf, &inArgs, &outArgs)) != AACENC_OK)
		{
			if (err == AACENC_ENCODE_EOF)
			{
				break;
			}

			return -1;
		}

		iAlreadyConsumeBytes = outArgs.numInSamples * 2;

		m_EncodedFrameSize += outArgs.numOutBytes;

		len -= iAlreadyConsumeBytes;
	}

	return 0;
}

int CAACEncoder::Init()
{
	if (aacEncOpen(&m_AAChandle, 0, m_ChannelMode) != AACENC_OK)
	{
		return -1;
	}

	//设置编码规格 AAC-LD AOT值为23
	if (aacEncoder_SetParam(m_AAChandle, AACENC_AOT, m_iEncodMode) != AACENC_OK)
	{
		return -1;
	}

	//设置采样率
	if (aacEncoder_SetParam(m_AAChandle, AACENC_SAMPLERATE, m_iAudioSample) != AACENC_OK)
	{
		return -1;
	}

	//设置声道数
	if (aacEncoder_SetParam(m_AAChandle, AACENC_CHANNELMODE, m_ChannelMode) != AACENC_OK)
	{
		return -1;
	}

	//设置声道顺序,默认填 1
	if (aacEncoder_SetParam(m_AAChandle, AACENC_CHANNELORDER, 1) != AACENC_OK)
	{
		return -1;
	}

	//bits/s
	//设置码率
	if (aacEncoder_SetParam(m_AAChandle, AACENC_BITRATE, m_iAudioBitrate) != AACENC_OK)
	{
		return -1;
	}

	if (23 == m_iEncodMode)
	{
		//设置封装格式为LATM
		if (aacEncoder_SetParam(m_AAChandle, AACENC_TRANSMUX, TT_MP4_LATM_MCP1) != AACENC_OK)
		{
			return -1;
		}
	}
	else
	{
		return -1;
	}

	if (aacEncoder_SetParam(m_AAChandle, AACENC_AFTERBURNER, 0) != AACENC_OK)
	{
		return -1;
	}

	//设置编码时采样点的个数
	if (aacEncoder_SetParam(m_AAChandle, AACENC_GRANULE_LENGTH, AAC_MAX_SAMPLE_CNT) != AACENC_OK)
	{
		return -1;
	}

	//设置复用个数为2
	if (aacEncoder_SetParam(m_AAChandle, AACENC_TPSUBFRAMES, 2) != AACENC_OK)
	{
		return -1;
	}

	if (aacEncEncode(m_AAChandle, NULL, NULL, NULL, NULL) != AACENC_OK)
	{
		return -1;
	}

	//编码的参数信息
	AACENC_InfoStruct EncoderInfo;
	if (aacEncInfo(m_AAChandle, &EncoderInfo) != AACENC_OK)
	{
		return -1;
	}

	//编码后一帧的最大字节数
	m_iEncoderBufferSize = EncoderInfo.maxOutBufBytes;

	return 0;
}

void CAACEncoder::Destroy()
{
	aacEncClose(&m_AAChandle);
}

解码

解码示例用于对AAC-LD,封装格式为LATM复用码流的解码

头文件

#ifndef AAC_DECODE_H
#define AAC_DECODE_H
#include "libAACdec/include/aacdecoder_lib.h"
class CAACDecoder
{
private:

public:
	CAACDecoder();
	~CAACDecoder();
	int	Init();
	int SetCodecParameters(int Mode, int frameSize);
	int	DecodeFrame(unsigned char* pEncodedFrame, unsigned int encodedFrameLen);

	int GetDecodedDataLen(unsigned char*& pExpandedFrame, int& sample);
private:
	int InternalDecodeFrame(unsigned char* pEncodedFrame, unsigned int encodedFrameLen);
	void Destroy();
private:
	int m_frameSize;

	unsigned char* m_pDecodedData;
	int m_iDecodeFrameSize;

	short* m_pOut;

	HANDLE_AACDECODER m_DecoderHandle;
	//2->AAC-LC 5->HE-AAC 29->HE-AAC-v2 23->AAC-LD 39->AAC-ELD
	int m_iEncodMode;
	int m_AudioSample;
#ifdef DECODE_RECORD_FILE
	FILE *m_PCMFile;
	FILE *m_FromEncoder;
#endif
};
#endif

cpp文件

#include "AACDecoder.h"

#define MB_AAC_IN_BUFF 96000

#define AAC_MONO_SILENCE_FRAME_SIZE 10
#define AAC_STEREO_SILENCE_FRAME_SIZE 11

CAACDecoder::CAACDecoder() :
	m_frameSize(0)
	, m_AudioSample(0)
	, m_pDecodedData(NULL)
	, m_iDecodeFrameSize(0)
	, m_pOut(NULL)
#ifdef DECODE_RECORD_FILE
	, m_FromEncoder(NULL)
	, m_PCMFile(NULL)
#endif
{
}

CAACDecoder::~CAACDecoder()
{
	Destroy();
}

int CAACDecoder::SetCodecParameters(int Mode, int frameSize)
{
	if (23 != Mode)
	{//AAC-LD 23
		return -1;
	}

	m_iEncodMode = Mode;
	m_frameSize = frameSize;
	return 0;
}

int CAACDecoder::GetDecodedDataLen(unsigned char*& pExpandedFrame, int& sample)
{
	pExpandedFrame = m_pDecodedData;
	sample = m_AudioSample;
	int tmp = m_iDecodeFrameSize;
	m_iDecodeFrameSize = 0;
	return tmp;
}

int CAACDecoder::DecodeFrame(unsigned char* pEncodedFrame, unsigned int encodedFrameLen)
{
#ifdef DECODE_RECORD_FILE
	fwrite(pEncodedFrame, encodedFrameLen, 1, m_FromEncoder);
#endif

	unsigned int tmp = encodedFrameLen;
	unsigned char* pData = pEncodedFrame;
	while (encodedFrameLen > 0)
	{//对复用的情况,一个RTP会携带多个audio frame,所以这里是个循环
		int iLen = InternalDecodeFrame(pData, encodedFrameLen);
		if (iLen < 0)
		{
			return -1;
		}

		pData += iLen;
		encodedFrameLen -= iLen;
	}

	return 0;
}

int CAACDecoder::Init()
{
	//LATM的封装格式
	m_DecoderHandle = aacDecoder_Open(TT_MP4_LATM_MCP1, 1);
	if (!m_DecoderHandle)
	{
		return -1;
	}

	if (aacDecoder_SetParam(m_DecoderHandle, AAC_CONCEAL_METHOD, 0) != AAC_DEC_OK)
	{
		return -1;
	}

#ifdef DECODE_RECORD_FILE
	m_PCMFile = fopen("./audiodata/aacdecoder_pcm", "wb");
	m_FromEncoder = fopen("./audiodata/aacfromencoder", "wb");
#endif

	m_pDecodedData = new unsigned char[MB_AAC_IN_BUFF];
	m_pOut = new short[MB_AAC_IN_BUFF];

	m_iDecodeFrameSize = 0;
	return 0;
}

int CAACDecoder::InternalDecodeFrame(unsigned char* pEncodedFrame, unsigned int encodedFrameLen)
{
	unsigned int uBufferSize = MB_AAC_IN_BUFF;
	unsigned int valid = encodedFrameLen;

	unsigned int iEncodedFrameLen = encodedFrameLen;
	AAC_DECODER_ERROR err;
	err = aacDecoder_Fill(m_DecoderHandle, &pEncodedFrame, &iEncodedFrameLen, &valid);
	if (err != AAC_DEC_OK)
	{
		return -1;
	}

	err = aacDecoder_DecodeFrame(m_DecoderHandle, m_pOut, uBufferSize/*/ sizeof(INT_PCM)*/, 0);
	if (err != AAC_DEC_OK)
	{
		return -1;
	}

	//获取解码后码流的信息
	CStreamInfo* info = aacDecoder_GetStreamInfo(m_DecoderHandle);
	if (!info)
	{
		return -1;
	}


	if (info->frameSize > 0)
	{//frameSize为解码后的长度,为固定的几类值,与编码端的设置相关
		unsigned char* pDecodedData = m_pDecodedData + m_iDecodeFrameSize;

		//采样率
		m_AudioSample = info->sampleRate;
		//声道*音频帧大小为整个帧的大小
		int outputlen = info->numChannels*info->frameSize;
		//将16位(INT_PCM为16位)转换为8位
		for (int i = 0; i < outputlen; i++)
		{
			unsigned char* out = &pDecodedData[sizeof(INT_PCM)*i];
			unsigned j;
			for (j = 0; j < sizeof(INT_PCM); j++)
				out[j] = (unsigned char)(m_pOut[i] >> (8 * j));
		}

		//16位转换为8位是长度大小需要乘以2
		m_iDecodeFrameSize += info->numChannels*info->frameSize * 2;
#ifdef DECODE_RECORD_FILE
		fwrite(pDecodedData, info->numChannels*info->frameSize * 2, 1, m_PCMFile);
#endif
	}

	return encodedFrameLen - valid;
}


void CAACDecoder::Destroy()
{

	if (m_pDecodedData)
	{
		delete[]m_pDecodedData;
		m_pDecodedData = NULL;
	}

	if (m_pOut)
	{
		delete[] m_pOut;
		m_pOut = NULL;
	}

	aacDecoder_Close(m_DecoderHandle);

#ifdef DECODE_RECORD_FILE
	fclose(m_PCMFile);
	m_PCMFile = NULL;

	fclose(m_FromEncoder);
	m_FromEncoder = NULL;
#endif
	return;
}




2016-12-22 16:14:37 mandagod 阅读数 3793

一些AAC编解码库


- FAAC, FAAD/FAAD2    ---编码只有AAC LC


- neroAACenc ---非商业可以使用(提供编码工具)


- FFmpeg's native AAC encoder(part of libavcodec), experimental by the developers as of December 2010   ---只有AAC LC,且质量不好


- libvo_aacenc, the Android VisualOn AAC encoder   ---只有AAC LC, opencore-amr-vo-aacenc,只有编码


- libfdk-aac, the Fraunhofer FDK AAC libray  ---包含到HE-AACv2,且性能较好   opencore-amr-fdk-aac


- libaacplus, 3GPP released reference implementations 3GPP High Efficiency Advanced Audio Codec (HE-AAC) Codec (3GPP TS 26.410 V 8.0.0), “Enhanced aacPlus general audio codec; Floating-point ANSI-C code”, 编解码都有

官网地址:https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1459

 一个libaacplus encoder第三方修改的增强博客:http://tipok.org.ua/node/17


-libaacplus, 作为ffmpeg的encoder补充,最新的ffmpeg看到对其放弃了支持

http://tipok.org.ua/node/17

2018-03-13 18:02:25 zftzftzft 阅读数 1566

写在前面

如果大家对音视频相关知识感兴趣,可以订阅我的专题 视频播放器和音视频基础知识

正文

1:AAC解码整体流程简述

AAC解码流程图

在主控模块开始运行后,主控模块将AAC比特流的一部分放入输入缓冲区,通过查找同步字得到一帧的起始,找到后,根据ISO/IEC 13818-7所述的语法开始进行Noisless Decoding(无噪解码),无噪解码实际上就是哈夫曼解码,通过反量化(Dequantize)、联合立体声(Joint Stereo),知觉噪声替换(PNS),瞬时噪声整形(TNS),反离散余弦变换(IMDCT),频段复制(SBR)这几个模块之后,得出左右声道的PCM码流,再由主控模块将其放入输出缓冲区输出到声音播放设备。

2:主控模块

主控模块的主要任务是操作输入输出缓冲区,调用其它各模块协同工作。其中,输入输出缓冲区均由DSP控制模块提供接口。输出缓冲区中将存放的数据为解码出来的PCM数据,代表了声音的振幅。它由一块固定长度的缓冲区构成,通过调用DSP控制模块的接口函数,得到头指针,在完成输出缓冲区的填充后,调用中断处理输出至I2S接口所连接的音频ADC芯片(立体声音频DAC和DirectDrive耳机放大器)输出模拟声音。

3. 同步及元素解码

同步及元素解码模块主要用于找出格式信息,并进行头信息解码,以及对元素信息进行解码。这些解码的结果用于后续的无噪解码和尺度因子解码模块。

AAC的音频文件格式有以下两种:

ADIF:Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。

ADTS:Audio Data Transport Stream 音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。

AAC的ADIF格式见下图:
ADIF格式

 3.1 ADIF的组织结构

AAC的ADTS的一般格式见下图:ADTS一般格式

3.2 ADTS的组织结构

图中表示出了ADTS一帧的简明结构,其两边的空白矩形表示一帧前后的数据。ADIF和ADTS的header是不同的。它们分别如下所示:
ADTS一帧的结构

3.3 ADIF的头信息

ADIF头信息

3.4 ADTS的固定头信息

ADTS固定头信息

 3.5 帧同步

帧同步目的在于找出帧头在比特流中的位置,13818-7规定,aac ADTS格式的帧头为12比特的“1111 1111 1111”.

3.6 头信息解码

ADTS的头信息为两部分组成,其一为固定头信息,紧接着是可变头信息。固定头信息中的数据每一帧都相同,而可变头信息则在帧与帧之间可变。

3.7 元素信息解码

在AAC中,原始数据块的组成可能有六种不同的元素。它们分别是

  • SCE: Single Channel Element单通道元素。单通道元素基本上只由一个ICS组成。一个原始数据块最可能由16个SCE组成。

  • CPE: Channel Pair Element 双通道元素,由两个可能共享边信息的ICS和一些联合立体声编码信息组成。一个原始数据块最多可能由16个SCE组成。

  • CCE: Coupling Channel Element 藕合通道元素。代表一个块的多通道联合立体声信息或者多语种程序的对话信息。
  • LFE: Low Frequency Element 低频元素。包含了一个加强低采样频率的通道。
  • DSE: Data Stream Element 数据流元素,包含了一些并不属于音频的附加信息。
  • PCE: Program Config Element 程序配置元素。包含了声道的配置信息。它可能出现在ADIF 头部信息中。
  • FIL: Fill Element 填充元素。包含了一些扩展信息。如SBR,动态范围控制信息等。

3.8 处理流程

(1). 判断文件格式,确定为ADIF或ADTS

(2). 若为ADIF,解ADIF头信息,跳至第6步。

(3). 若为ADTS,寻找同步头。

(4). 解ADTS帧头信息。

(5). 若有错误检测,进行错误检测。

(6). 解块信息。

(7). 解元素信息。

4.无噪声解码

无噪编码就是哈夫曼编码,它的作用在于进一步减少尺度因子和量化后频谱的冗余,即将尺度因子和量化后的频谱信息进行哈夫曼编码。

全局增益编码成一个8位的无符号整数,第一个尺度因子与全局增益值进行差分编码后再使用尺度因子编码表进行哈夫曼编码。后续的各尺度因子都与前一个尺度因子进行差分编码。

量化频谱的无噪编码有两个频谱系数的划分。其一为4元组和2元组的划分,另一个为节划分。对前一个划分来说,确定了一次哈夫曼表查找出的数值是4个还是2个。对后一个划分来说,确定了应该用哪一个哈夫曼表,一节中含有若干的尺度因子带并且每节只用一个哈夫曼表。

4.1 分段

无噪声编码将输入的1024个量化频谱系数分为几个段(section),段内的各点均使用同一个哈夫曼表,考虑到编码效率,每一段的边界最好同尺度因子带的边界重合。所以每一段必段传送信息应该有:段长度,所在的尺度因子带,使用的哈夫曼表。

4.2 分组和交替

分组是指忽略频谱系数所在窗,将连续的,具有相同尺度因子带的频谱系数分为一组放在一起,共享一个尺度因子从而得到更好的编码效率。这样做必然会引起交替,即本来是以
c[组][窗][尺度因子带][ 系数索引]

为顺序的系数排列,变为将尺度因子带同的系数放在一起:c[组][尺度因子带][窗][ 系数索引]这样就引起了相同窗的系数的交替。

4.3大量化值的处理

大量化值在AAC中有两种处理方法:在哈夫曼编码表中使用escape标志或使用脉冲escape方法。前者跟mp3编码方法相似,在许多大量化值出现时采用专门的哈夫曼表,这个表暗示了它的使用将会在哈夫曼编码后面跟跟一对escape值及对值的符号。在用脉冲escape方法时,大数值被减去一个差值变为小数值,然后使用哈夫曼表编码,后面会跟一个脉冲结构来帮助差值的还原。

无噪解码的流程图如下:

无噪解码的流程图

5.尺度因子解码及逆量化

在aac编码中,逆量化频谱系数是由一个非均匀量化器来实现的,在解码中需进行其逆运算。即保持符号并进行4/3次幂运算。

在频域调整量化噪声的基本方法就是用尺度因子来进行噪声整形。尺度因子就是一个用来改变在一个尺度因子带的所有的频谱系数的振幅增益值。使用尺度因子这种机制是为了使用非均匀量化器在频域中改变量化噪声的比特分配。

5.1 尺度因子带(scalefactor-band)

频率线根据人耳的听觉特性被分成多个组,每个组对应若干个尺度因子,这些组就叫做尺度因子带。为了减少信息含有短窗的边信息,连续的短窗可能会被分为一组,即将若干个短窗当成一个窗口一起传送,然后尺度因子将会作用到所有分组后的窗口去。

5.2 反量化公式

 x_invquant = sign(x_quant) * | x_quant| ^(4/3)

其中:

x_invquant 表示反量化的结果
sign (x) 表示反量化的结果
^ 标识幂运算

5.3 应用尺度因子公式:

x_rescal = x_invquant * gain
gain = 2 ^ (0.25 * (sf – SF_OFFSET))

其中:

x_rescal 为应用了尺度因子公式之后的值
gain 为一个增益
sf 为尺度因子值
SF_OFFSET 为一个常数,设为100

6、联合立体声解码

联合立体声有两种,M/S stereo(中间旁道立体声)和intensity stereo(强度立体声)

6.1 M/S stereo

在M_S立体声模式中,传送的是规格化的中间/旁边声道的信息,计算公式如下:

l, r 表示转换后的左右声道值
m 表示中间声道值
s 表示旁边声道值

6.2 Intensity stereo

在强度立体声模式中,左声道传的是幅值,右声道的scalefactor传的是立体声的位置is_pos。如果仅在一个指定了common_window为1的CPE中的右通道中指定哈夫曼表为INTENSITY_HCB或INTENSITY_HCB2,则解码时使用强度立体声模式。其计算公式如下:

is_pos += dpcm_is_pos
scale = invert_intensity * 0.5 ^(0.25 * ispos)
r_spec = scale * l_spec

从完全备份中还原,从完全备份中还原数据库非常简单

is_po 是右声道传送的scalefactor
dpcm_is_pos 是上一个is_pos,初值为0
scale 为强度因子
invert_intensity 为是否反转哈夫曼表(表14和表15)这个变量由ms_used指定,关系为:
invert_intensity = 1 – 2 * ms_used,
另外,当ms_mask_present为0时,invert_intensity恒为1。

6.3 处理流程

联合立体声解码流程图

7、PNS

PNS(Perceptual Noise Substitution) 知觉噪声替换模块是一种以参数编码的方式模拟噪声的模块。在判别出音频值中的噪声后,将些噪声不进行量化编码,而是采用一些参数告诉解码器端这是某种噪声,然后解码器端将会对这些噪声用一些随机的编码来制造出这一类型的噪声。

在具体操作上,PNS模块对每个尺度因子带侦测频率4kHz以下的信号成分。如果这个信号既不是音调,在时间上也无强烈的能量变动,就被认为是噪声信号。其信号的音调及能量变化都在心理声学模型中算出。

在解码中,如果发现使用了哈夫曼表13(NOISE_HCB),则表明使用了PNS。

由于M/S立体声解码与PNS解码互斥,故可以用参数ms_used来表明是否两个声道都用同样的PNS。如果ms_used参数为1,则两个声道会用同样的随机向量来生成噪声信号。

PNS的能量信号用noise_nrg来表示,如果使用了PNS,则能量信号将会代替各自的尺度因子来传送。

噪声能量编码同尺度因子一样,采用差分编码的方式。第一个值同样为全局增益值。它同强度立体声位置值及尺度因子交替地放在一起,但对差分解码来说又彼此忽略。即下一个噪声能量值以上一个噪声能量值而不是强度立体声位置或尺度因子为标准差分解码。

随机能量将会在一个尺度因子带内产生noise_nrg所计算出的平均能量分布。

7.1 处理流程

PNS解码流程图

8、TNS

TNS瞬态噪声整形用于控制一个转换窗口内的瞬时噪声形态。它是用一个对单个通道的滤波过程来实现的。

传统的变换编码方案常常遇到信号在时域变化非常剧烈的问题,特别是语音信号,这个问题是因为量化后的噪声分布虽然在频率域上得到控制,但在时域上却以一个常数分布在一个转换块内。如果这种块中信号变化得很剧烈却又不转向一个短块去,那这个常数分布的噪声将会被听到。

TNS的原理利用了时域和频域的二元性和LPC(线性预测编码)的时频对称性,即在其中的任意一个域上做编码与在另一域上做预测编码等效,也就是说,在一个域内做预测编码可以在另一域内增加其解析度。量化噪声产生是在频域产生的,降低了时域的解析度,故在这里是在频域上做预测编码。

在AACplus中,由于基于AAC profile LC,故TNS的滤波器阶数被限制在12阶以内。

8.1 处理流程

TNS解码流程图

9. IMDCT

将音频数据从频域转换到时域的过程主要是由将频域数据填入一组IMDCT滤波器来实现的。在进行IMDCT变换后,输出数值经过加窗,叠加,最后得到时域数值。

9.1 IMDCT公式

IMDCT公式

    for 0<= n <N
n 采样点索引值
i 窗索引值
k 频谱系数索引值
N 窗函数长度,全为短窗N = 256,其余情况为2048

n0=(N/2 +1)/2

9.2 块型

由于长块的频域分辨率较高而短块的时域分辨率较高,故长块较适合相对平稳的时域信号,而短块较适合变化相对较快的时域信号。

长块长度为2048个点,短块长度为256个点。

9.3 加窗

AAC用到两种窗函数,分别为Kaiser-Bessel 类(KBD)窗和正弦窗。

KBD窗如下所示:

KBD窗
定义为:

   for 0 <= n <= N/2


使用KBD窗时,window_shape 为1

正弦窗如下所示:

定义如下图:

使用正弦窗时,window_shape 为0

另定义:


对应于四种不同的窗序列分别进行不同的加窗变换:
1.) 仅有长块:
window_shape 为 1:

window_shape 为0:

加窗后,时域信号可用w(n)表示为:

2.) 长开始块:

window_shape 为 1:


window_shape 为0:

加窗后,时域信号可用w(n)表示为:

3.) 只有短块:
window_shape 为 1:

window_shape 为0:

加窗后,时域信号可用w(n)表示为:

2.) 长结束块:
window_shape 为 1:

window_shape 为0

加窗后,时域信号可用w(n)表示为:

9.4 覆盖叠加

在加窗完成后得到的时域信号值z经过前后窗相互叠加计算,得出最后的PCM值:

术语说明

AAC Advanced Audio Coding 高级音频编码
AAC LC AAC with Low Complexity AAC的低复杂度配置
AAC plus HE-AAC, AAC+,MPEG4 AAC LC加入SBR模块后形成的一个aac版本
MPEG Motion Picture Expert Group
IMDCT 反离散余弦变换
ADIF Audio Data Interchange Format 音频数据交换格式
ADTS Audio Data Transport Stream 音频数据传输流
SCE Single Channel Element单通道元素
CPE Channel Pair Element 双通道元素
CCE Coupling Channel Element 藕合通道元素
DSE Data Stream Element 数据流元素
PCE Program Config Element 程序配置元素
FIL Fill Element 填充元素
ICS Individual Channel Stream 独立通道流
PNS Perceptual Noise Substitution 知觉噪声替换
SBR Spectral Band Replication 频段复制
TNS Temporal Noise Shaping 瞬时噪声整形
ch channel 通道
2019-12-29 22:57:41 fanyun_01 阅读数 170

        AAC,全称Advanced Audio Coding,是一种专为声音数据设计的文件压缩格式。与MP3不同,它采用了全新的算法进行编码,更加高效,具有更高的“性价比”。利用AAC格式,可使人感觉声音质量没有明显降低的前提下,更加小巧。

AAC解码流程图:

2018-03-29 11:31:17 audio_algorithm 阅读数 3041

一、ADTS格式:

ADTS的全称是Audio Data Transport Stream。是AAC音频的传输流格式。

1. adts_sequence()

{

 while (nextbits() == syncword) {

 adts_frame();

 }

}

2. adts_frame()

{
 adts_fixed_header();

 adts_variable_header();

 if (number_of_raw_data_blocks_in_frame == 0) {

 adts_error_check();

 raw_data_block();

 }

 else {

 adts_header_error_check();

 for (i = 0; i <= number_of_raw_data_blocks_in_frame; i++) {

 raw_data_block();

 adts_raw_data_block_error_check();

 }

 }

}

3.

adts_fixed_header()

{
 syncword; 12 bslbf

 ID; 1 bslbf

 layer; 2 uimsbf

 protection_absent; 1 bslbf

 profile; 2 uimsbf

 sampling_frequency_index; 4 uimsbf

 private_bit; 1 bslbf

 channel_configuration; 3 uimsbf

 original/copy; 1 bslbf

 home; 1 bslbf

}

adts_variable_header()

{
 copyright_identification_bit; 1 bslbf

 copyright_identification_start; 1 bslbf

 frame_length; 13 bslbf

 adts_buffer_fullness; 11 bslbf

 number_of_raw_data_blocks_in_frame; 2 uimsfb

}

详细说明下ADTS头的重要数据部分:

syncword 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始。

ID MPEG 标示符, 设置为1.

layer Indicates which layer is used. Set to ‘00’

protection_absent 表示是否误码校验

profile 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC

sampling_frequency_index 表示使用的采样率下标

sampling_frequency_index sampling frequeny [Hz]

0x0 96000

0x1 88200

0x2 64000

0x3 48000

0x4 44100

0x5 32000

0x6 24000

0x7 22050

0x8 16000

0x9 2000

0xa 11025

0xb 8000

0xc reserved

0xd reserved

0xe reserved

0xf reserved

channel_configuration 表示声道数

frame_length 一个ADTS帧的长度包括ADTS头和raw data block.

adts_buffer_fullness 0x7FF 说明是码率可变的码流

number_of_raw_data_blocks_in_frame

表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.

所以说number_of_raw_data_blocks_in_frame == 0 表示说ADTS帧中有一个AAC数据块并不是说没有。


二、编码算法处理流程

   首先对输入的PCM信号分段,每帧每声道1024个样本,采用1/2重叠,组合得到2048个样本。加窗后,进行离散余弦变化(MDCT),输出1024个频谱分量,依据不同采样率和变换块类型划分成10个不同带宽的比例因子频带。其中变化块类型由 心理声学模型 计算分析得到,该模型还将输出 信掩比,用于后续模块的处理。

    AAC 还使用了一种新的称为时域噪声整形的技术,简称为 TNS。 TNS 的作用机理在于利用了时域和频域信号的对偶性。在立体声编码方面, AAC 既支持 M/S,又支持L/R,两者的选择准则是看比特数的消耗。基于人耳对高频的定位主要取决于能量的特点,采用了增强立体声(IS)技术,对耦合声道只传一路包络。经过前述多个模块的预处理后,在量化和编码阶段才真正降低了数据量使用非均匀量化来改善小信号的信噪比,把比例因子频带合并成分区后再对频谱分量进行霍夫曼编码。量化和编码使用一种两层嵌套循环算法,以权衡码率和失真之间的矛盾。最后,进行比特流封装,得到压缩后的码流


AAC各个模块计算量的比重

                   

   由此可见,量化编码、心理声学模型以及滤波器组模块是编码算法中复杂度、计算量较高的模块。


AAC编解码简介

阅读数 3295

AAC编解码

阅读数 0