LAME 是目前非常优秀的一种 MP3 编码引擎,在业界转码成 MP3 格式的音频文件时,最常用的编码器就是 LAME 库。用 LAME 的源码通过交叉编译就能得到 SO 库(这一部分在交叉编译那篇文章已经有完整的过程了,此处不再赘述),现在只需要把 SO 库集成到我们自己的项目中即可,需要做的就是编写好接口,上层调用即可。
LAME
PCM 当达到 320Kbit/s 以上时,LAME 编码出来的音频质量几乎可以和 CD 的音质相媲美,并且还能保证整个音频文件的体积非常小,因此若要在移动端平台上编码 MP3 文件,使用 LAME 便成为唯一的选择。
MP3 参数
mp3(MPEG Layer III)这种格式在生活中很常见,但是 mp3 有很多种参数,这里讨论一下 mp3 编码所必须知道的一些参数。
- 采样率(sampleRate):采样率越高声音的还原度越好。
- 比特率(bitrate):每秒钟的数据量,越高音质越好。
- 声道数(channels):声道的数量,通常只有单声道和双声道,双声道即所谓的立体声。
- 比特率控制模式:ABR、VBR、CBR,这 3 中模式含义很容易查询到,不在赘述。
MPEG 有几个版本的协议,不同版本的协议能够支持的参数能力是不同的。编码库的使用者必须清楚不同版本的区别才能正确的设置参数。
有以下 3 个版本的协议,MPEG1、MPEG2、MPEG2.5。其中 MPEG2.5 是非官方的标准,但是流传广泛,所以基本也都支持。他们的区别主要集中在支持的比特率和采样率不同。
采样率支持 (Hz)
MPEG1 | MPEG2 | MPEG2.5 |
---|---|---|
44100 | 22050 | 11025 |
48000 | 24000 | 12000 |
32000 | 16000 | 8000 |
比特率支持 (bit/s)
MPEG1 | MPEG2 | MPEG2.5 |
---|---|---|
32 | 8 | 8 |
40 | 16 | 16 |
48 | 24 | 24 |
56 | 32 | 32 |
64 | 40 | 40 |
80 | 48 | 48 |
96 | 56 | 56 |
112 | 64 | 64 |
128 | 80 | |
160 | 96 | |
192 | 112 | |
224 | 128 | |
256 | 144 | |
320 | 160 |
编码 Mp3 基本流程
编码 mp3 基本上遵循以下的流程
初始化编码参数
lame_init
:初始化一个编码参数的数据结构,给使用者用来设置参数。
设置编码参数
lame_set_in_samplerate
:设置被输入编码器的原始数据的采样率。lame_set_out_samplerate
:设置最终 mp3 编码输出的声音的采样率,如果不设置则和输入采样率一样。lame_set_num_channels
:设置被输入编码器的原始数据的声道数。lame_set_mode
:设置最终 mp3 编码输出的声道模式,如果不设置则和输入声道数一样。参数是枚举,STEREO
代表双声道,MONO
代表单声道。lame_set_VBR
:设置比特率控制模式,默认是 CBR,但是通常我们都会设置 VBR。参数是枚举,vbr_off
代表 CBR,vbr_abr
代表 ABR(因为 ABR 不常见,所以本文不对 ABR 做讲解)vbr_mtrh
代表 VBR。lame_set_brate
:设置 CBR 的比特率,只有在 CBR 模式下才生效。lame_set_VBR_mean_bitrate_kbps
:设置 VBR 的比特率,只有在 VBR 模式下才生效。
其中每个参数都有默认的配置,如非必要可以不设置。这里只介绍了几个关键的设置接口,还有其他的设置接口可以参考 lame.h
(lame 的文档里只有命令行程序的用法,没有库接口的用法)。
初始化编码器器
lame_init_params
:根据上面设置好的参数建立编码器
编码 PCM 数据
lame_encode_buffer
或lame_encode_buffer_interleaved
:将 PCM 数据送入编码器,获取编码出的 mp3 数据。这些数据写入文件就是 mp3 文件。- 其中
lame_encode_buffer
输入的参数中是双声道的数据分别输入的,lame_encode_buffer_interleaved
输入的参数中双声道数据是交错在一起输入的。具体使用哪个需要看采集到的数据是哪种格式的,不过现在的设备采集到的数据大部分都是双声道数据是交错在一起。 - 单声道输入只能使用
lame_encode_buffer
,把单声道数据当成左声道数据传入,右声道传 NULL 即可。 - 调用这两个函数时需要传入一块内存来获取编码器出的数据,这块内存的大小 lame 给出了一种建议的计算方式:采样率 / 20+7200。
销毁编码器
lame_close
销毁编码器,释放资源。
编码器参数
对于编码器的参数设置,所能接受的参数值并不是任意的。上面表格中列出了编码器器能够支持的参数值,如果我们设置的参数值不在其中,那么编码器会自动帮我们选择一个最近的值。但是每个版本支持的参数范围不一致,假如设置了 MPEG2 的比特率又设置了 MPEG1 的采样率那么会发生什么?
Lame 库会优先服从输出采样率的设置,根据采样率选择协议版本,然后在这个版本所能支持的比特率中选一个和设置比特率最接近的。
1 | /* |
编译 Mp3 实践
准备 PCM 文件
需要先准备 PCM 文件放在手机的公共存储区,lame 的源码包里有一个 testcase.wav,其实这个 wav 去掉前面的提出 44 位的 wav 前缀,就是 pcm 文件,通过代码去除前 44 位的 wav 前缀,修改后缀名即可得到 testcase.pcm
1 | public class WAV2PCM { |
现在得到了 PCM 文件,也就是原始音频文件。
编写 JNI 层代码
新建一个 C++ 的 Native 工程,删除 native-lib.cpp,开始编写我们自己的调用库,app 目录树如下:
1 | ├── assert |
lame 的 so 库得拷贝过来,头文件同样必不可少。
Mp3Encoder.java
1 | package com.xxx.mp3encode; |
通过 javah 命令生成头文件 Mp3Encoder.h,然后稍作修改:
1 |
|
Mp3Encoder.cpp
1 |
|
编写 CMakeLists
CMakeLists.txt
1 | # CMake 最小版本 |
配置 Gradle
build.gradle(app)
1 | plugins { |
编写上层代码
MainActivity.java
1 | public class MainActivity extends AppCompatActivity { |
AndroidManifest.xml
1 |
|
OK,点击按钮触发编码 Mp3,Success!