11、音频处理:Opus编解码、回声消除(AEC)、噪声抑制(NS)、自动增益控制(AGC)
音频处理,说实在的,是视频会议里最容易被忽视、却又最影响体验的环节。你想想看,画面稍微模糊点大家还能忍,但要是声音断断续续、有回声、或者突然炸麦,那会议基本就开不下去了。我在早期做一款会议APP时,就吃过这个亏——画面调得贼清晰,结果用户反馈说「听不清对方说话」,最后不得不返工重做音频模块。
今天我们就来聊聊Android平台上WebRTC的音频处理四件套:Opus编解码、回声消除(AEC)、噪声抑制(NS)、自动增益控制(AGC)。这四个东西,说白了就是保证「声音清晰、稳定、无干扰」的基石。
核心要点:音频处理不是独立存在的,它们是一个流水线。麦克风采集 → AEC → NS → AGC → Opus编码 → 网络传输 → Opus解码 → 播放。任何一个环节出问题,都会影响最终效果。
11.1 Opus编解码:音频的「万能胶」
Opus这个编码器,我个人非常喜欢。它不像AAC那样有专利壁垒,也不像Speex那样音质一般。Opus是真正的「全能选手」——从语音到音乐,从低码率到高码率,它都能搞定。
我记得有一次项目需要支持从6kbps到128kbps的动态码率调整,当时团队里有人提议用AAC+AMR双编码器切换。我直接说:用Opus一个就够了。为什么?因为Opus内部集成了SILK(语音编码)和CELT(音频编码)两种模式,可以无缝切换。
| 特性 | Opus | AAC | AMR-NB |
|---|---|---|---|
| 码率范围 | 6 kbps ~ 510 kbps | 8 kbps ~ 320 kbps | 4.75 ~ 12.2 kbps |
| 采样率 | 8 kHz ~ 48 kHz | 8 kHz ~ 96 kHz | 8 kHz |
| 算法延迟 | 5 ms ~ 65 ms | 20 ms ~ 100 ms | 20 ms |
| 专利费用 | 免费 | 需授权 | 需授权 |
| 适用场景 | 语音+音乐 | 音乐为主 | 语音仅窄带 |
在WebRTC中,Opus是默认的音频编码器。你不需要手动集成,但有几个参数我建议你关注一下:
// 设置Opus编码参数示例
PeerConnectionFactory factory = ...;
AudioSource audioSource = factory.createAudioSource(audioConstraints);
// 关键参数设置
MediaConstraints constraints = new MediaConstraints();
// 设置音频编码码率(单位:kbps)
constraints.optional.add(
new MediaConstraints.KeyValuePair("googAudioBitrate", "32"));
// 启用立体声(默认是单声道)
constraints.optional.add(
new MediaConstraints.KeyValuePair("googStereo", "true"));
// 设置音频编码复杂度(0-10,越高越耗CPU)
constraints.optional.add(
new MediaConstraints.KeyValuePair("googAudioCodingComplexity", "6"));
我的经验:在移动端,建议将码率控制在24-48kbps之间。32kbps是个甜点值——音质够用,带宽占用也不大。如果你做的是音乐类应用,可以开到64kbps以上,但要注意CPU消耗。
11.2 回声消除(AEC):别让用户听到自己的声音
回声消除,说白了就是「让扬声器里播放的对方声音,不要被麦克风再次采集回来」。你想想看,如果AEC没做好,对方说一句「你好」,你这边麦克风又录到了这句「你好」并传回去,对方就会听到自己的回声——这体验简直灾难。
WebRTC的AEC模块是基于自适应滤波器的。它会把扬声器播放的参考信号和麦克风采集的信号做对比,然后动态地消除回声。嗯,这里要注意:AEC需要知道「扬声器在播什么」,所以它需要拿到音频播放的参考信号。
在Android上,AEC的配置通常通过AudioProcessing模块来完成:
// 创建AudioProcessing实例
AudioProcessing audioProcessing = AudioProcessing.create();
// 配置AEC
AudioProcessing.Config config = audioProcessing.getConfig();
// 启用回声消除
config.echo_canceller.enabled = true;
// 设置回声消除模式(0-3,越高越激进)
config.echo_canceller.mobile_mode = true; // 移动端推荐启用
audioProcessing.setConfig(config);
// 在音频采集回调中处理
audioProcessing.processStream(audioFrame);
我曾经踩过的坑:在部分Android设备上,如果同时开启了硬件AEC和WebRTC软件AEC,会导致双重处理,反而产生更严重的回声。我的建议是:先检测设备是否支持硬件AEC,如果支持,就关闭WebRTC的软件AEC。检测方法很简单——看设备是否有AcousticEchoCanceler这个类。
11.3 噪声抑制(NS):过滤掉键盘声和空调声
噪声抑制,就是去掉背景中的非人声部分。比如键盘敲击声、空调嗡嗡声、马路上的车流声。WebRTC的NS模块采用的是谱减法——它先估计出噪声的频谱特征,然后从原始信号中减去这部分。
我个人习惯把NS的强度设置为中等。太弱了,噪声去不干净;太强了,人声也会变得「闷闷的」,像隔着枕头说话一样。
// 配置噪声抑制
config.noise_suppression.enabled = true;
// 设置抑制等级:0=低, 1=中, 2=高, 3=极高
config.noise_suppression.level = 1; // 推荐中等
audioProcessing.setConfig(config);
小技巧:如果你发现NS处理后的人声有「音乐感」或「水声」,可以尝试降低抑制等级,或者调整采样率。48kHz采样率下NS效果通常比16kHz好,但CPU消耗也更大。
11.4 自动增益控制(AGC):让声音大小刚刚好
AGC的作用,是让说话声音大的人不会「炸麦」,说话声音小的人不会被「淹没」。它本质上是一个动态范围压缩器——把过大的信号压下来,把过小的信号提上去。
我记得有一次测试,一个同事离麦克风很远,声音特别小;另一个同事离麦克风很近,声音又特别大。如果没有AGC,远的人听不见,近的人刺耳。开了AGC之后,两个人的声音都能保持在合适的音量范围。
// 配置自动增益控制
config.gain_controller1.enabled = true;
// 设置目标增益(单位:dBFS,推荐-18到-15)
config.gain_controller1.target_level_dbfs = -15;
// 设置最大增益(单位:dB,推荐12-18)
config.gain_controller1.max_gain_db = 15;
// 启用自适应模式
config.gain_controller1.adaptive_mode = true;
audioProcessing.setConfig(config);
注意:AGC不要设置得太激进。最大增益超过20dB时,底噪会被明显放大。我曾经在一个项目中把最大增益设到了30dB,结果安静环境下能听到明显的「嘶嘶」声。后来改回15dB,问题就解决了。
11.5 四个模块的协同工作
这四个模块不是各自为战的,它们有严格的顺序:
- 先AEC:消除回声,因为回声是最大的干扰源
- 再NS:去除背景噪声,让信号更干净
- 最后AGC:调整音量,因为前两步处理完后信号更纯净,AGC才能准确判断
如果你把顺序搞反了——比如先AGC再AEC——AGC会把回声也放大,导致AEC处理压力剧增,效果大打折扣。
在WebRTC的native层,这个流水线是自动执行的。你只需要通过AudioProcessing配置好参数,它就会按照正确的顺序处理每一帧音频数据。
总结一下:音频处理是视频会议APP的「隐形功臣」。Opus负责高效编码,AEC负责消除回声,NS负责过滤噪声,AGC负责统一音量。这四个模块配合好了,用户才能获得「面对面聊天」般的体验。我建议你在开发时,先用默认参数跑通流程,然后根据实际测试结果微调——毕竟每个设备的麦克风和扬声器特性都不一样。
公众号:蓝海资料掘金营,微信deep3321