音视频基础(二)上:编码之AAC解析
上文我们分析了音视频的本质,并且了解了其基本的采集方式:YUV格式的图片以及PCM
在文章末尾我们简单的计算了音频和视频文件一分钟的大小,结果是惊人的大。为什么我们平时看到的音视频文件并没有这么大呢?因为它们采用了不同的压缩编码来进行压缩。
什么是压缩编码
编码,是信息从一种形式变成另一种形式的过程。
在上篇文章中,我们介绍PCM的生成过程中就有一步是编码,那里的编码就是将量化后的整数转换为二进制格式,是一种非常简单的编码。
而我们本篇文章将要讲的编码,是编码中的一种:压缩编码。顾名思义,它的主要作用就是压缩原本的信息。
例子:哈夫曼编码
举个例子:哈夫曼编码是一种著名的压缩编码,在本文介绍的主题AAC中也使用了哈夫曼编码。哈夫曼编码的核心就是:一些元素(可以是字符或者任意其他东西)出现的概率比别的大,那么就给他们更短的编码,从而实现整体的压缩。
比如有一段话:'do or do not'其中:
javascript
const obj = {
'o': 4,
' ': 3,
'd': 2,
'n': 1,
't': 1,
'r': 1
}
根据出现频率可以构建哈夫曼树,然后根据树的路径可以得到对应的码字:
字符 | 哈夫曼编码 |
---|---|
o | 00 |
(空格) | 01 |
d | 100 |
r | 101 |
n | 110 |
t | 111 |
具体过程请自己学习哈夫曼编码相关内容,如Huffman Coding
需要注意的是:哈夫曼编码并不唯一,所以在解压时需要发送树/指定同一个树/发送足够信息让对方能推导树
所以最后'do or do not'被编码成为 10000010 01010110 00001110 00111,原本需要 12 * 8 = 96 比特的字符串被编码成了29比特,压缩了2/3
这种没有损失任何信息的压缩被称为 无损压缩 ,主要就是根据信息本身的各种特征进行的压缩。但是这种压缩非常依赖信息本身结构,就如上文哈夫曼编码,如果所有信息出现频率都是一样的,它就没有办法压缩。有时候我们可能需要去除掉一些信息来进行压缩,这种压缩被称为 有损压缩。
为什么能有损压缩
有人可能会觉得,怎么可以去除信息呢?信息都是非常重要的。是的,对于传输过程来说,保证信息的完整是非常关键的,除非 信息丢失了,但是接收者感觉不到,仍然认为其是完整的。或者用专业一点的话来讲: 信息并没有失真 ,我们去除掉的只是 冗余
对于音频来说,冗余信号包含人耳听觉范围外的音频信号以及被掩蔽掉的音频信号等。
例如,人耳所能察觉的声音信号的频率范围为20Hz~20KHz,除此之外的其它频率人耳无法察觉,都可视为冗余信号。
此外,根据人耳听觉的生理和心理声学现象,当一个强音信号与一个弱音信号同时存在时,弱音信号将被强音信号所掩蔽而听不见,这样弱音信号就可以视为冗余信号而不用传送。这就是人耳听觉的掩蔽效应,主要表现在频谱掩蔽效应和时域掩蔽效应。
频谱掩蔽效应
频谱掩蔽效应有两个方面:一、低于人耳听力阈值的信号被遮蔽;二、强音遮蔽弱音
时域掩蔽效应
而时域遮蔽效应则是强音哪怕没有和弱音同时出现,只是在其很短的时间内出现,也会出现遮蔽
AAC
AAC(A dvanced A udio C oding,译为:高级音频编码),是由Fraunhofer IIS、杜比实验室、AT&T、Sony、Nokia等公司共同开发的有损音频编码和文件格式。
AAC是一种高度成熟的压缩编码,我们实际工作中根本不需要也没有办法调整其内部实现的压缩算法。我们实际工作中了解AAC编码的主要目的只有一个:对编码的格式有一定了解额,从而能够实现自己将PCM编码为AAC(encoder)或者将AAC解码成PCM(decoder)
具体的编码解码操作会在之后的音视频实战篇介绍,这里只介绍AAC的格式。了解了格式,实际上有很多方法可以进行编码解码
编码
① 输入PCM参数
由于编码一般不是前端进行的,前端只负责解码,所以这里只简单介绍ffmpeg命令行用法
使用ffmpeg命令行生成AAC时,需要指定三个PCM的基本参数:采样率、声道数、存储格式(符号 + 位深度 + 字节序)
符号和字节序上篇未介绍,这里简单介绍:
- 采样数据是否有符号(Sign):要表达的就是字面上的意思,需要注意的是,使用有符号的采样数据不能用无符号的方式播放。
- 字节序:表示音频PCM数据存储的字节序是大端存储(big-endian)还是小端存储(little-endian),为了数据处理效率的高效,通常为小端存储。
如 存储格式为
s16le
,即为有符号(s),16bit位深度,小端存储(le)
sh
ffmpeg -ar 44100 -ac 2 -f s16le -i in.pcm -c:a libfdk_aac out.aac
② 选择AAC协议
再将PCM编码成AAC时,我们首先要选择一种AAC的规格------AAC为了适应不同复杂度的情况,有9种规格:
- MPEG-2 AAC LC:低复杂度规格(Low Complexity)
- MPEG-2 AAC Main:主规格
- MPEG-2 AAC SSR:可变采样率规格(Scaleable Sample Rate)
- MPEG-4 AAC LC:低复杂度规格(Low Complexity)
-
- 现在的手机比较常见的MP4文件中的音频部分使用了该规格
- MPEG-4 AAC Main:主规格
- MPEG-4 AAC SSR:可变采样率规格(Scaleable Sample Rate)
- MPEG-4 AAC LTP:长时期预测规格(Long Term Predicition)
- MPEG-4 AAC LD:低延迟规格(Low Delay)
- MPEG-4 AAC HE:高效率规格(High Efficiency)
其中常用的是LC和HE两种规格。HE规格相比LC规格的提升主要是增加了SBR(Spectral Band Substitution)其中MPEG-4相比MPEG-2的主要区别就增加了PNS(Perceptual Noise Substitution,感知噪音替代算法)
在编码时,需要根据据自身需求选择合适的协议。以 -profile
指定
sh
ffmpeg -i in.wav -c:a libfdk_aac -profile:a aac_he_v2 -b:a 32k out.aac
使用
ffmpeg -i
时,默认选择LC
③ 指定码流组织格式
AAC可以输出两种码流组织格式:ADIF和ADTS
ADIF:Audio Data Interchange Format 音频数据交换格式。这种格式的特征是可以确定的找到这个音频数据的开始,不需进行在音频数据流中间开始的解码,即它的解码必须在明确定义的开始处进行。故这种格式常用在磁盘文件中。
ADTS:Audio Data Transport Stream 音频数据传输流。这种格式的特征是它是一个有同步字的比特流,解码可以在这个流中任何位置开始。它的特征类似于mp3数据流格式。
简单说,ADTS可以在任意帧解码,也就是说它每一帧都有头信息。ADIF只有一个统一的头,所以必须得到所有的数据后解码。且这两种的header的格式也是不同的,目前一般编码后的和抽取出的都是ADTS格式的音频流。两者具体的组织结构如下所示:
使用ffmpeg直接输出的AAC格式都是ADIF,如果想要变成ADTS需要字节进行处理,可以参考视频文件解复用获取的aac文件添加adts头,因为较为复杂这里不再进行介绍
可以类比为 MP4和 fMP4
④ 指定输出文件格式
AAC编码的文件格式可以是 m4a,aac,mp4三种。我们可以任意指定
sh
# m4a
ffmpeg -i in.wav -c:a libfdk_aac out.m4a
# mp4
ffmpeg -i in.wav -c:a libfdk_aac out.mp4
解码
解码实际上就是编码的逆操作,如果继续使用ffmpeg的话非常简单,只需要调用aac文件中声明的解码器就可以了:
sh
ffmpeg -c:a libfdk_aac -i in.aac -f s16le out.pcm
如果要使用js进行解码,可以使用一些开源项目如acc.js
未来可能会解析aac.js的源码。