Audio Echo Cancellation not working on AVAudioSessionCategoryPlayAndRecord and AVAudioSessionModeDefault

270 views
Skip to first unread message

Tan Pham Anh

unread,
Feb 21, 2020, 6:44:20 PM2/21/20
to discuss-webrtc
Hi,

How can I avoid audio echo when make a Webrtc Call using AVAudioSessionCategoryPlayAndRecord and AVAudioSessionModeDefault.
If I switch AVAudioSessionCategoryPlayAndRecord and AVAudioSessionModeVideoChat, echo cancellation working normally but my AVAudioPlayer has a small volume. So I changed audio mode to AVAudioSessionModeDefault and echo cancellation is gone :(

Henrik Andreasson

unread,
Feb 22, 2020, 11:42:07 AM2/22/20
to discuss-webrtc
iOS only supports built-in AEC in the modes you mentioned where echo is cancelled. To support echo cancellation for other modes, you must modify the WebRTC stack to enable its own software-based AEC instead. Might require some changes and no guarantees that it works well.

--

---
You received this message because you are subscribed to the Google Groups "discuss-webrtc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrt...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/discuss-webrtc/eda37b02-055f-4b12-a46d-f371f0e52d3a%40googlegroups.com.

Stan Tsouvallas

unread,
Mar 4, 2020, 2:21:55 AM3/4/20
to discuss-webrtc
I've noticed that apps using PeerConnectionFactory don't need to explicitly set the software AEC if there is no hardware AEC  (like signal: https://github.com/signalapp/ringrtc/blob/d816012799dfacf9c9b69f30b7cf2bdcac57b78c/src/android/api/org/signal/ringrtc/CallManager.java#L766). Is there some logic that is automatically enabling the software AEC in this case? 

I'm manually creating my AudioDeviceModule, CallState and Call, and if I don't explicitly set the following there will be no echo cancellation on devices without hardware AEC:

AudioProcessing::Config APconfig;
 
APconfig.noise_suppression.enabled = true;
 
APconfig.echo_canceller.enabled = true;
 
APconfig.high_pass_filter.enabled = true;
 audioProcessing
->ApplyConfig(APconfig);





On Sunday, February 23, 2020 at 3:42:07 AM UTC+11, Henrik Andreasson wrote:
iOS only supports built-in AEC in the modes you mentioned where echo is cancelled. To support echo cancellation for other modes, you must modify the WebRTC stack to enable its own software-based AEC instead. Might require some changes and no guarantees that it works well.

On Sat, Feb 22, 2020 at 12:44 AM Tan Pham Anh <tanph...@gmail.com> wrote:
Hi,

How can I avoid audio echo when make a Webrtc Call using AVAudioSessionCategoryPlayAndRecord and AVAudioSessionModeDefault.
If I switch AVAudioSessionCategoryPlayAndRecord and AVAudioSessionModeVideoChat, echo cancellation working normally but my AVAudioPlayer has a small volume. So I changed audio mode to AVAudioSessionModeDefault and echo cancellation is gone :(

--

---
You received this message because you are subscribed to the Google Groups "discuss-webrtc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to discuss...@googlegroups.com.

Henrik Andreasson

unread,
Mar 4, 2020, 3:42:47 AM3/4/20
to discuss-webrtc
See third_party/webrtc/sdk/android/api/org/webrtc/audio/JavaAudioDeviceModule.java for how it works in the official WebRTC demo application on Android.
If no HW AEC exists, we fallback to the WebRTC based AEC. It is possible to override the default behaviour and always use WebRTC's AEC.

/**
* Control if the built-in HW acoustic echo canceler should be used or not. The default is on if
* it is supported. It is possible to query support by calling
* isBuiltInAcousticEchoCancelerSupported().
*/
public Builder setUseHardwareAcousticEchoCanceler(boolean useHardwareAcousticEchoCanceler) {
if (useHardwareAcousticEchoCanceler && !isBuiltInAcousticEchoCancelerSupported()) {
Logging.e(TAG, "HW AEC not supported");
useHardwareAcousticEchoCanceler = false;
}
this.useHardwareAcousticEchoCanceler = useHardwareAcousticEchoCanceler;
return this;
}


To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrt...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/discuss-webrtc/b8faaec4-e5f0-41ca-9d1d-6de559cc3393%40googlegroups.com.

Stan Tsouvallas

unread,
Mar 4, 2020, 4:58:38 AM3/4/20
to discuss-webrtc
I've seen that code however what I'm doing is slightly more low level and doesn't use the Java API. I'm looking in my logs and seeing that useHardwareAcousticEchoCanceler = false; is being propagated to the AudioManager:

I/org.webrtc.Logging: WebRtcAudioEffects: canUseAcousticEchoCanceler: false
I/org.webrtc.Logging: WebRtcAudioEffects: canUseNoiseSuppressor: false
W/org.webrtc.Logging: WebRtcAudioManager: AAudio support is currently disabled on all devices!
I/audio_manager.cc: (line 276): OnCacheAudioParameters: hardware_aec: 0, hardware_agc: 0, hardware_ns: 0, low_latency_output: 0, low_latency_input: 0, pro_audio: 0, a_audio: 0, sample_rate: 48000, output_channels: 1, input_channels: 1, output_buffer_size: 1924, input_buffer_size: 1920

But the software AEC is definitely not initialized. I've added my own logs to your code and I see that I'm hitting this block: https://source.chromium.org/chromium/chromium/src/+/master:third_party/webrtc/modules/audio_processing/audio_processing_impl.cc;l=1743?q=audio_processing_impl.c&ss=chromium&originalUrl=https:%2F%2Fcs.chromium.org%2F.

The code that I'm using to setup the audio device is below.

 webrtc::Config config;
 config
.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(false));
 rtc
::scoped_refptr<webrtc::AudioProcessing> audioProcessing = AudioProcessingBuilder().Create(config);

 webrtc
::AudioDeviceModule::AudioLayer audioLayer = webrtc::AudioDeviceModule::kPlatformDefaultAudio;

 
auto audioDeviceModule = _audioDeviceDataObserver ?
         webrtc
::CreateAudioDeviceWithDataObserver(
             audioLayer
, getWebRTCTaskQueueFactory(), _audioDeviceDataObserver.get()) :
         webrtc
::AudioDeviceModule::Create(audioLayer, getWebRTCTaskQueueFactory());

 
constexpr uint16_t kDefaultDeviceId = 0;
 
bool available = false;

 
if (audioDeviceModule->Init() != 0) {
     LOG_E
("Failed to initialize the AudioDeviceModule.");
     
throw MediaException("Failed to initialize the AudioDeviceModule.");
 
}

 
if (audioDeviceModule->SetPlayoutDevice(kDefaultDeviceId) != 0) {
     LOG_E
("Failed to activate audio playout device.");
     
throw MediaException("Failed to activate audio playout device.");
 
}

 
if (audioDeviceModule->InitSpeaker() != 0)
     LOG_W
("Failed to initialize speaker.");

 
if (audioDeviceModule->StereoPlayoutIsAvailable(&available) != 0)
     LOG_W
("Failed to query stereo playout.");

 
if (audioDeviceModule->SetStereoPlayout(available) != 0)
     LOG_W
("Failed to enable stereo playout.");

 
if (audioDeviceModule->SetRecordingDevice(kDefaultDeviceId) != 0) {
     LOG_E
("Failed to activate audio recording device.");
     
throw MediaException("Failed to activate audio recording device.");
 
}

 
if (audioDeviceModule->InitMicrophone() != 0)
     LOG_W
("Failed to initialize microphone.");

 
if (audioDeviceModule->StereoRecordingIsAvailable(&available) != 0)
     LOG_W
("Failed to query stereo recording.");

 
if (audioDeviceModule->SetStereoRecording(available) != 0)
     LOG_W
("Failed to enable stereo recording.");

 
if (audioDeviceModule->BuiltInAECIsAvailable() && audioDeviceModule->EnableBuiltInAEC(true) != 0) {
     LOG_I
("succesfully enabled built-in AEC");
 
} else {
     LOG_E
("failed to enable built-in AEC");
 
}

 
if (audioDeviceModule->BuiltInAGCIsAvailable() && audioDeviceModule->EnableBuiltInAGC(true) != 0) {
     LOG_I
("succesfully enabled built-in AGC");
 
} else {
     LOG_E
("failed to enable built-in AGC");
 
}

 
if (audioDeviceModule->BuiltInNSIsAvailable() && audioDeviceModule->EnableBuiltInNS(true) != 0) {
     LOG_I
("succesfully enabled built-in NS");
 
} else {
     LOG_E
("failed to enable built-in NS");
 
}

 
AudioState::Config audioStateConfig;
 audioStateConfig
.audio_mixer = AudioMixerImpl::Create();
 audioStateConfig
.audio_processing = audioProcessing;
 audioStateConfig
.audio_device_module = audioDeviceModule;
 
auto audioState = AudioState::Create(audioStateConfig);

I've also confirmed that audioDeviceModule->BuiltInAECIsAvailable(); is returning false. 

Is there something obvious that I'm missing? If I manually apply the config using the following code then it works as expected, but from what you've said above I don't understand why the software AEC isn't enabled automatically. 

AudioProcessing::Config APconfig;
APconfig.echo_canceller.enabled = true;
audioProcessing
->ApplyConfig(APconfig);

Henrik Andreasson

unread,
Mar 4, 2020, 5:51:41 AM3/4/20
to discuss-webrtc
It is very difficult to say exactly what happens in your case since you are making changes in a rather complex code base and you are replacing large parts of the default code.
I would start by injecting an exact copy of the default ADM and verify that it works first. Next, I would gradually replace the working injected ADM with the version you need.
IMHO, I would avoid making too large changes in the default ADM (Java based) since it has been developed over a long period of time and it is far from trivial to replace.

Just an idea, it is stated that:

I've also confirmed that audioDeviceModule->BuiltInAECIsAvailable(); is returning false. 

Note that, this API must be based on calling real Java based APIs that asks the actual device using [1].


To unsubscribe from this group and stop receiving emails from it, send an email to discuss-webrt...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/discuss-webrtc/5b0078e5-70d2-461a-a278-46e955c29b01%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages