disable hardware AEC on some devices. fixes #3734

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java | 73 
1 file changed, 48 insertions(+), 25 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java 🔗

@@ -8,7 +8,6 @@ import android.util.Log;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.Futures;
@@ -41,6 +40,8 @@ import org.webrtc.SessionDescription;
 import org.webrtc.SurfaceTextureHelper;
 import org.webrtc.VideoSource;
 import org.webrtc.VideoTrack;
+import org.webrtc.audio.JavaAudioDeviceModule;
+import org.webrtc.voiceengine.WebRtcAudioEffects;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -57,6 +58,22 @@ public class WebRTCWrapper {
 
     private static final String EXTENDED_LOGGING_TAG = WebRTCWrapper.class.getSimpleName();
 
+    //we should probably keep this in sync with: https://github.com/signalapp/Signal-Android/blob/master/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java#L296
+    private static final Set<String> HARDWARE_AEC_BLACKLIST = new ImmutableSet.Builder<String>()
+            .add("Pixel")
+            .add("Pixel XL")
+            .add("Moto G5")
+            .add("Moto G (5S) Plus")
+            .add("Moto G4")
+            .add("TA-1053")
+            .add("Mi A1")
+            .add("Mi A2")
+            .add("E5823") // Sony z5 compact
+            .add("Redmi Note 5")
+            .add("FP2") // Fairphone FP2
+            .add("MI 5")
+            .build();
+
     private static final int CAPTURING_RESOLUTION = 1920;
     private static final int CAPTURING_MAX_FRAME_RATE = 30;
 
@@ -165,6 +182,30 @@ public class WebRTCWrapper {
         this.eventCallback = eventCallback;
     }
 
+    private static void dispose(final PeerConnection peerConnection) {
+        try {
+            peerConnection.dispose();
+        } catch (final IllegalStateException e) {
+            Log.e(Config.LOGTAG, "unable to dispose of peer connection", e);
+        }
+    }
+
+    @Nullable
+    private static CapturerChoice of(CameraEnumerator enumerator, final String deviceName, Set<String> availableCameras) {
+        final CameraVideoCapturer capturer = enumerator.createCapturer(deviceName, null);
+        if (capturer == null) {
+            return null;
+        }
+        final ArrayList<CameraEnumerationAndroid.CaptureFormat> choices = new ArrayList<>(enumerator.getSupportedFormats(deviceName));
+        Collections.sort(choices, (a, b) -> b.width - a.width);
+        for (final CameraEnumerationAndroid.CaptureFormat captureFormat : choices) {
+            if (captureFormat.width <= CAPTURING_RESOLUTION) {
+                return new CapturerChoice(capturer, captureFormat, availableCameras);
+            }
+        }
+        return null;
+    }
+
     public void setup(final Context context, final AppRTCAudioManager.SpeakerPhonePreference speakerPhonePreference) throws InitializationException {
         try {
             PeerConnectionFactory.initialize(
@@ -186,9 +227,15 @@ public class WebRTCWrapper {
         Preconditions.checkState(this.eglBase != null);
         Preconditions.checkNotNull(media);
         Preconditions.checkArgument(media.size() > 0, "media can not be empty when initializing peer connection");
+        final boolean setUseHardwareAcousticEchoCanceler = WebRtcAudioEffects.canUseAcousticEchoCanceler() && !HARDWARE_AEC_BLACKLIST.contains(Build.MODEL);
+        Log.d(Config.LOGTAG, String.format("setUseHardwareAcousticEchoCanceler(%s) model=%s", setUseHardwareAcousticEchoCanceler, Build.MODEL));
         PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder()
                 .setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglBase.getEglBaseContext()))
                 .setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true))
+                .setAudioDeviceModule(JavaAudioDeviceModule.builder(context)
+                        .setUseHardwareAcousticEchoCanceler(setUseHardwareAcousticEchoCanceler)
+                        .createAudioDeviceModule()
+                )
                 .createPeerConnectionFactory();
 
 
@@ -258,14 +305,6 @@ public class WebRTCWrapper {
         }
     }
 
-    private static void dispose(final PeerConnection peerConnection) {
-        try {
-            peerConnection.dispose();
-        } catch (final IllegalStateException e) {
-            Log.e(Config.LOGTAG, "unable to dispose of peer connection", e);
-        }
-    }
-
     synchronized void verifyClosed() {
         if (this.peerConnection != null
                 || this.eglBase != null
@@ -469,22 +508,6 @@ public class WebRTCWrapper {
         }
     }
 
-    @Nullable
-    private static CapturerChoice of(CameraEnumerator enumerator, final String deviceName, Set<String> availableCameras) {
-        final CameraVideoCapturer capturer = enumerator.createCapturer(deviceName, null);
-        if (capturer == null) {
-            return null;
-        }
-        final ArrayList<CameraEnumerationAndroid.CaptureFormat> choices = new ArrayList<>(enumerator.getSupportedFormats(deviceName));
-        Collections.sort(choices, (a, b) -> b.width - a.width);
-        for (final CameraEnumerationAndroid.CaptureFormat captureFormat : choices) {
-            if (captureFormat.width <= CAPTURING_RESOLUTION) {
-                return new CapturerChoice(capturer, captureFormat, availableCameras);
-            }
-        }
-        return null;
-    }
-
     public PeerConnection.PeerConnectionState getState() {
         return requirePeerConnection().connectionState();
     }