每个游戏里都会有很多音效和背景音乐,优化包体积时音频文件是一个很容易收效而且容易被忽略的点。
一、概念解释
下面几个概念是在处理音频的时候经常遇到的,而且理解上有很多误区,搞清楚这些概念是十分必要的。
文件格式 & 编码格式
首先需要解释一个概念,大家往往认为媒体文件文件的扩展名就是其编码格式,对图片文件来说是成立的,但是音频和视频文件就不大正确了。文件扩展名一般代表的是媒体文件的文件格式,或者叫容器格式。一种容器格式可以支持很多编码格式,也可能只支持特定的编码格式,有以下3种情况:
- 容器格式支持只支持特定编码格式,比如RealMedia文件格式(rm/rmvb)只支持RealMediad编码。ASF容器和WMA、WMV编码的关系也是如此。
- 容器格式支持很多不同的编码格式,比如MP4容器支持MPEG-1/MPEG-2/MPEG-4等视频编码格式,AAC、MP3、MP2、MP1等音频编码格式。类似的还有MKV、3GP。
- 容器格式支持不同的编码,但是现实里绝大多数情况只和特定的编码格式组合。比如WAV虽然支持使用不同的压缩编码,但是绝大多数WAV文件都是无压缩的PCM(LPCM)编码。OGG容器一般也只有Vorbis编码会用。
注:WAV、AVI的文件格式其实都是RIFF格式,扩展名只是指明了媒体类型。
我们关心的是编码格式,因为编码格式决定了音频的大小、质量,找到合适的编码格式后,很容易就能确定用什么容器去装它。常见的音频容器格式有WAV、AIFF、CAF、MP4(m4a),从平台支持的和容器中选择能容纳所选编码格式的就行了。
采样率 & 采样精度 & 比特率
采样率指的是将声音的波形数字化时,采样的频率,或者说是每秒钟内采样点的个数。
采样精度指的是每个采样点占用的位数,采样精度为8bit时,可以把波形分成256级不同的信号(对应视频里的概念,采样率相当于帧率,采样精度相当于分辨率)
比特率指的是音频文件每秒占用的bit数
比特率(bit/s) = 采样率(Hz) <span>* 采样精度(bit) *</span> 声道数 <span>* 压缩率 *</span> 1s
比如最常见的wav文件,采样率是44.1KHz,采样精度16bit,双声道,没有压缩,那么它的比特率:
比特率 = 44100 <span>* 16 *</span> 2 * 1 = 1411200 bps = 1411.2 kbps
二、编码格式比较
我们选择音频格式的时候希望在保证一定音频质量的前提下,压缩率尽可能高。目前主流的有损压缩编码为MP3、AAC、HE-AAC、Vorbis。从技术上来说AAC和Vorbis远远领先于MP3,尤其是在较低比特率(<=96kbps)下,相同比特率的AAC和Vorbis音质要远远好于MP3。
MP3目前仍然非常流行,原因一是MP3的Codec太普及,二是大多数的用户没有追求高压缩率的需求,一般用户使用的mp3歌曲的比特率为192kbps,在这种比较高的比特率下,各种编码格式的质量区别并不明显。
编码器试听测试列表这个wiki里有很多试听测试结果,可以反映音频格式+Codec的质量。所谓试听测试,就是准备好一些不同特点的源音频,然后用各个待测编码器编码,找一批试听者在不知道音频文件的编码的情况下,根据试听感觉给每个编码后的音频打分。我们选出低比特率,并且包含AAC和Vorbis的测试:
- 64kbit/sec stereo multiformat listening test
- public multiformat listening test @ 64 kbps (March/April 2011)
- public multiformat listening test (July 2014)
测试的结论基本一致 AAC(Apple Codec)>= Vorbis。
AAC编码
AAC编码有9种很多不同的规格Profile,其中AAC-LC(低复杂度规格)是最常见的规格,AAC-HE(高效率规格)是压缩比最高的规格,是我们优化音频大小的主力编码。AAC-HE有分为HE-ACC v1和HE-AAC v2两个版本,使用了SBR(频段复制)和PS(参量立体声)的两种音频编码技术,达到了非常高的压缩比。感兴趣的话可以看这篇文档,其中详细介绍了SBR和PS的工作原理。
Android & iOS 上的情况
Android可以使用的编码格式为MP3、AAC、HE-AAC、Vorbis、AMR、Linear PCM、ALAC
iOS可以使用的编码格式为MP3、AAC、HE-AAC、IMA4、iLBC、Linear PCM、FLAC
其中iLBC和AMR是为Speech设计的,也就是专门用于存储人声录音;LPCM、ALAC、FLAC是无损格式;AAC、MP3、HE-AAC、Vorbis是有损压缩。
iOS Tips
音频格式 | 硬解码 | 软解码 |
---|---|---|
AAC | Yes | Yes, starting in iOS 3.0 |
ALAC | Yes | Yes, starting in iOS 3.0 |
HE-AAC | Yes | No |
iLBC | No | Yes |
iMA4 | No | Yes |
Linear PCM | No | Yes |
MP3 | Yes | Yes, starting in iOS 3.0 |
µ-law / a-law | No | Yes |
硬解码相对于软解码的优势在于非常低的CPU消耗,也就意味着低耗电、低发热。
Linear PCM和iMA4虽然没有硬解,但是同时播放多个Linear PCM和iMA4不会造成高CPU消耗,因为LPCM没有被压缩而iMAC4(ADPCM)的压缩算法非常简单。
AAC、HE-AAC、ALAC和MP3硬解码使用相同的硬件路径,并且该解码器不支持同时解码多个音频,所以当这几种格式的音频同时播放时,只有一个音频使用硬解码,其他都是软解码。HE-AAC格式由于不能软解码,同时只能有一个HE-AAC音频实例在播放。
Other Tips
- Android和iOS可以使用不同的音频格式,用工具转换;
- 音乐、音效可以使用不同的格式;
- 要考虑设备系统兼容性,Android >= 2.3,iOS >= 5.0,不同版本的系统环境中必须都有所选格式decoder;
- 压缩格式音频文件解码可能会占用可观的CPU资源,尤其是多个同时播放的音效,可以在首次游戏加载的时候将它们解码成非压缩格式(Linear PCM);
- 编码器和解码器(Codec)的实现会影响音频的质量,相同的编码格式不同的编码器产生的音频文件质量可能相差很远(从前面的几个测试结果可以看出,同样的HE-AAC编码,FAAC的质量要比Apple AAC差得多)。
三、我们使用的方案
我们几乎所有的项目中都使用了这个方案:iOS和Android分别转码成HE AAC v1和HE AAC v2,体积缩小到原来的1/2、1/3。先将所有的音频转成HE AAC v2,然后将其中高频失真比较明显的换成HE AAC v1。
Codec | 优化前 | 优化后 |
---|---|---|
HE AAC | 150k(128kbps mp3) | 87k(65kbps HE AAC v1, FDK AAC) |
HE AAC v2 | 150k(128kbps mp3) | 45k(33Kbps HE AAC v2, Apple AAC) |
转化工具
Codec使用的是iTunes里的HE AAC v2 Codec,这个Codec的效果比我试过的所有的HE AAC v2 Codec(包括)都要好非常多。网上有一个封装好直接可以用的可执行文件enhAacPlusEncenhAacPlusEnc,配合下面的脚本即可完成转换:
#!/bin/bash
ROOTDIR="$( cd "$( dirname "$0" )" && pwd )"
cd $ROOTDIR/..
rm -rf ../tmp
mkdir -p tmp/raw
mkdir -p tmp/aac
cd song_new;
for mp3 in *.MP3; do
#mp3="../tmp/raw/${mp3/MP3/mp3}"
wav="../tmp/raw/${mp3/%MP3/wav}"
aac="../tmp/aac/${mp3/MP3/aac}"
m4a="../song_android/${mp3/MP3/m4a}"
m4a2="../song_ios/${mp3/MP3/m4a}"
# decode mp3 to wav
ffmpeg -y -i "$mp3" "$wav"
#For Android: encode wav in he-aac-v2 using Apple AAC codec
../tools/enhAacPlusEnc "$wav" "$aac" 32000 s
# warp raw adts aac into mp4 container for android 2 compatibility
mp4box -add "$aac" "$m4a"
#For iOS: encode wav in he-aac-v1 using FDK AAC codec
ffmpeg -i "$wav" -c:a libfdk_aac -profile:a aac_he -b:a 64k "$m4a2";
done
#rm -rf ../tmp