跳到主要内容

Android WebRTC 回声消除

使用 Android 原生消除回声时,如果使用不当会造成某些机型是无效的。以下代码在三星 s20 、vivo 的某些机型就会出现无效的现象。

原因:有些机型默认音频模式是MODE_NORMAL,而不是MODE_IN_COMMUNICATION

解决:必须通过AudioManager设置模式为MODE_IN_COMMUNICATION,并且AudioRecord的模式也设置成MODE_IN_COMMUNICATION,代码如下:

// 获取 AudioManager 实例
AudioManager audioManager = (AudioManager) main.context.getSystemService(Context.AUDIO_SERVICE);
// 设置音频模式为通信模式
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
// 设置扬声器打开
audioManager.setSpeakerphoneOn(true);

// 并且 AudioRecord 的模式也设置成 MODE_IN_COMMUNICATION
// new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION,...)

问题背景

在 WebRTC 音视频通话中,回声消除(AEC, Acoustic Echo Cancellation)是确保通话质量的关键。Android 设备碎片化严重,不同厂商的音频驱动实现差异大,导致回声消除在某些机型上失效。

常见失效机型

  • 三星 Galaxy S20 系列
  • vivo X/Y 系列部分机型
  • OPPO Reno 系列部分机型
  • 小米 Redmi 低端机型

根因分析

Android 的回声消除依赖 AudioEffect 框架中的 AcousticEchoCanceler。但该功能的有效性取决于音频通路的配置:

  • MODE_NORMAL:普通媒体播放模式,AEC 不会生效
  • MODE_IN_COMMUNICATION:VoIP 通信模式,系统会启用硬件/软件 AEC
  • AudioSource 必须为 VOICE_COMMUNICATION 才能正确路由到 AEC 处理链

💡 关键点:AudioManager 的 mode 和 AudioRecord 的 source 必须同时设置为通信模式,缺一不可。

完整解决方案

public class WebRTCAudioManager {
private AudioManager audioManager;
private int savedAudioMode;

public void enableAEC(Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

// 保存当前模式,通话结束后恢复
savedAudioMode = audioManager.getMode();

// 设置为通信模式
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);

// 根据需要设置扬声器
audioManager.setSpeakerphoneOn(true);

// 请求音频焦点
audioManager.requestAudioFocus(null,
AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}

public AudioRecord createAudioRecord(int sampleRate, int bufferSize) {
// 使用 VOICE_COMMUNICATION 作为音频源
return new AudioRecord(
MediaRecorder.AudioSource.VOICE_COMMUNICATION,
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize
);
}

public void checkAECSupport() {
if (AcousticEchoCanceler.isAvailable()) {
Log.d("AEC", "硬件 AEC 可用");
} else {
Log.w("AEC", "硬件 AEC 不可用,需要软件实现");
}
}

public void release() {
if (audioManager != null) {
audioManager.setMode(savedAudioMode);
audioManager.abandonAudioFocus(null);
}
}
}

软件 AEC 方案(兜底)

对于硬件 AEC 不可用的设备,可使用软件方案:

  • WebRTC 内置的 AECM(移动端优化版 AEC)
  • Speex 的 AEC 实现
  • RNNoise(基于 RNN 的噪声/回声抑制)

调试技巧

  • 使用 adb shell dumpsys audio 查看当前音频路由
  • 使用 AudioEffect.Descriptor[] 检查设备支持的音效
  • 在不同机型上录音对比,确认 AEC 是否生效
  • 注意蓝牙耳机连接时音频路由会改变,需要额外处理