disable hardware AEC on some devices. fixes #3734

This commit is contained in:
Daniel Gultsch 2020-05-21 11:13:46 +02:00
parent de941f6036
commit a2a7256682
1 changed files with 48 additions and 25 deletions

View File

@ -8,7 +8,6 @@ import android.util.Log;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
@ -41,6 +40,8 @@ import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper; import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoSource; import org.webrtc.VideoSource;
import org.webrtc.VideoTrack; import org.webrtc.VideoTrack;
import org.webrtc.audio.JavaAudioDeviceModule;
import org.webrtc.voiceengine.WebRtcAudioEffects;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -57,6 +58,22 @@ public class WebRTCWrapper {
private static final String EXTENDED_LOGGING_TAG = WebRTCWrapper.class.getSimpleName(); 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_RESOLUTION = 1920;
private static final int CAPTURING_MAX_FRAME_RATE = 30; private static final int CAPTURING_MAX_FRAME_RATE = 30;
@ -165,6 +182,30 @@ public class WebRTCWrapper {
this.eventCallback = eventCallback; 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 { public void setup(final Context context, final AppRTCAudioManager.SpeakerPhonePreference speakerPhonePreference) throws InitializationException {
try { try {
PeerConnectionFactory.initialize( PeerConnectionFactory.initialize(
@ -186,9 +227,15 @@ public class WebRTCWrapper {
Preconditions.checkState(this.eglBase != null); Preconditions.checkState(this.eglBase != null);
Preconditions.checkNotNull(media); Preconditions.checkNotNull(media);
Preconditions.checkArgument(media.size() > 0, "media can not be empty when initializing peer connection"); 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() PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder()
.setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglBase.getEglBaseContext())) .setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglBase.getEglBaseContext()))
.setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true)) .setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true))
.setAudioDeviceModule(JavaAudioDeviceModule.builder(context)
.setUseHardwareAcousticEchoCanceler(setUseHardwareAcousticEchoCanceler)
.createAudioDeviceModule()
)
.createPeerConnectionFactory(); .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() { synchronized void verifyClosed() {
if (this.peerConnection != null if (this.peerConnection != null
|| this.eglBase != 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() { public PeerConnection.PeerConnectionState getState() {
return requirePeerConnection().connectionState(); return requirePeerConnection().connectionState();
} }