灵活性
背景
在一些实时音视频系统中,终端软件通常可以设置音频编码参数,如采样率,通道数,码率等。开放编码参数可配,增加了灵活性,但是在实现增加了复杂度。特别是某些编码格式,如果考虑到音频的采集,回放,实时性。这里针对AAC的编解码库libfdk-aac来说说参数可配的限制及对应的策略。
libfdk-aac不同AAC规格的差异性
- 不通规格AAC编码输入帧的长度要求不一样,如下所示:
AACENC_GRANULE_LENGTH =
0x0105, /*!< Core encoder (AAC) audio frame length in samples:
- 1024: Default configuration.
- 512: Default length in LD/ELD configuration.
- 480: Length in LD/ELD configuration.
- 256: Length for ELD reduced delay mode (x2).
- 240: Length for ELD reduced delay mode (x2).
- 128: Length for ELD reduced delay mode (x4).
- 120: Length for ELD reduced delay mode (x4). */
AAC-ELD的范围是最广的,包括 1024,512,480,其次是AAC-LD范围为:1024,512,480,那么对AAC-LC来说就只有1024了。(注意这里的长度是采样点的个数)。
这个输入帧的长度会直接影响音频采集的采样率,因为在特定的采样率下,采集的音频帧的长度如果与编码要求输入的帧长相等或者是整数倍时,这样程序处理最简单。
例如:
在我前面的文章<< 使用libfdk-aac编码所需注意的细节 >> 介绍过,通过LATM格式封装AAC-LD时,设置的输入帧长度为480,采样点位数为2个字节时,则帧长字节数为960个字节。这时可以将音频采样率设置为48000,单声道。那么采集的20ms音频数据刚好是编码要求的输入长度的两倍。那么20ms的采样音频数据可以产生两个AAC-LD数据帧,打包时,一个RTP包就携带了两帧AAC-LD数据,刚好切合LATM格式复用的目的。
如果要兼容采样率可配,那就需要考虑采集帧长不满足编码器要求输入帧长的情况。再考虑RTP打包的要求,那么程序必定会更加复杂。(如果通过缓存队列来兼容采集输出与编码输入的要求,那么就会引入延迟)。
对AAC-LC而言,要求输入长度是1024,下面是libfdk-aac支持的采样率,每个采样率产生的长度都不能满足编码器要求的整数倍的要求。
AACENC_SAMPLERATE = 0x0103, /*!< Audio input data sampling rate. Encoder
supports following sampling rates: 8000, 11025,
12000, 16000, 22050, 24000, 32000, 44100,
48000, 64000, 88200, 96000 */
封装这种差异性还是比较困难的,因为在实时系统中,不单单是要考虑API的封装,还要考虑数据的采集及RTP封包,延迟的引入。
- 解决方式
我认为的最优的解决方式就是将AAC的支持格式及封包方式固化,比如就只支持AAC-LD , LATM封装模式,采样率为48000,ptime为20ms,不可配置。在媒体协商时也进行固化,比如在sdp中就标明只支持这一种套餐,如果没交集就协商失败。
依此类推到其它音频编码格式,可能会出现各种不同的局限。要满足音频参数全范围可配的灵活性,难度还是挺大的。其实从用户体验的角度,32k采样和48k采样在声音效果上,区别并不大。所以应该在满足业务需求的情况下来提供有限的灵活性。
- 一个参数配置例子
下面是某款华为IP电话的媒体配置界面
上图红框所示,对AAC就只支持AAC-LD(封装格式固化为LATM),并且采样率,声道,编码都不可配(固化为48000,单声道,96k)。对opus的编码则开放了采样率的配置。所以这种音频参数可配的原则应该采用利于实现的原则,开放有限的参数配置。
额外记录
LOAS与ADTS的判断
LOAS与ADTS结构比较相似,头+数据的形式。RTP打包都比较简单一个包携带一帧数据。如果有与别的系统互通的要求,可以通过RFC3640方式打包。
- ADTS封装格式的判断
ADTS 的头长为7个字节。Syncword长度为12 bits,值为0xFFF,代表一个ADTS帧的开始,作为分界符,用于同步每帧的起始位置。
if (packet[0] != 0xff || (packet[1] & 0xf0) != 0xf0)
{
//不为ADTS格式
}
ADTS帧长度的获取
packet_size = ((packet[3] & 0x03) << 11) | (packet[4] << 3) | (packet[5] >> 5);
packet指向ADTS的起始
- LOAS封装格式的判断
LOAS的头长为3字节。Syncword长度为11 bits,值为0x2b7
LOASFlag = packet[0]<<3|((packet[1]&0xe0)>>5);
if (LOASFlag != 0x2b7)
{
//不为LOAS格式
}
LOAS帧长度获取,Syncword的后13 bits为帧的剩余长度
packet_size = (packet[1]&0x1f)<<8 |packet[2];