Index: webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java |
diff --git a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java |
index e5408fd81b5bc36459f3f9a24eb1365b16970819..255e92d9ec98389a6bef34ee677d818f3020b5d9 100644 |
--- a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java |
+++ b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java |
@@ -42,6 +42,11 @@ class WebRtcAudioEffects { |
private static final UUID AOSP_NOISE_SUPPRESSOR = |
UUID.fromString("c06c8400-8e06-11e0-9cb6-0002a5d5c51b"); |
+ // Contains the available effect descriptors returned from the |
+ // AudioEffect.getEffects() call. This result is cached to avoid doing the |
+ // slow OS call multiple times. |
+ private static Descriptor[] cachedEffects = null; |
+ |
// Contains the audio effect objects. Created in enable() and destroyed |
// in release(). |
private AcousticEchoCanceler aec = null; |
@@ -60,22 +65,31 @@ class WebRtcAudioEffects { |
// Checks if the device implements Acoustic Echo Cancellation (AEC). |
// Returns true if the device implements AEC, false otherwise. |
public static boolean isAcousticEchoCancelerSupported() { |
+ // Note: we're using isAcousticEchoCancelerEffectAvailable() instead of |
+ // AcousticEchoCanceler.isAvailable() to avoid the expensive getEffects() |
+ // OS API call. |
return WebRtcAudioUtils.runningOnJellyBeanOrHigher() |
- && AcousticEchoCanceler.isAvailable(); |
+ && isAcousticEchoCancelerEffectAvailable(); |
} |
// Checks if the device implements Automatic Gain Control (AGC). |
// Returns true if the device implements AGC, false otherwise. |
public static boolean isAutomaticGainControlSupported() { |
+ // Note: we're using isAutomaticGainControlEffectAvailable() instead of |
+ // AutomaticGainControl.isAvailable() to avoid the expensive getEffects() |
+ // OS API call. |
return WebRtcAudioUtils.runningOnJellyBeanOrHigher() |
- && AutomaticGainControl.isAvailable(); |
+ && isAutomaticGainControlEffectAvailable(); |
} |
// Checks if the device implements Noise Suppression (NS). |
// Returns true if the device implements NS, false otherwise. |
public static boolean isNoiseSuppressorSupported() { |
+ // Note: we're using isNoiseSuppressorEffectAvailable() instead of |
+ // NoiseSuppressor.isAvailable() to avoid the expensive getEffects() |
+ // OS API call. |
return WebRtcAudioUtils.runningOnJellyBeanOrHigher() |
- && NoiseSuppressor.isAvailable(); |
+ && isNoiseSuppressorEffectAvailable(); |
} |
// Returns true if the device is blacklisted for HW AEC usage. |
@@ -115,7 +129,7 @@ class WebRtcAudioEffects { |
// AudioEffect.queryEffects() can throw IllegalStateException. |
@TargetApi(18) |
private static boolean isAcousticEchoCancelerExcludedByUUID() { |
- for (Descriptor d : AudioEffect.queryEffects()) { |
+ for (Descriptor d : getAvailableEffects()) { |
if (d.type.equals(AudioEffect.EFFECT_TYPE_AEC) && |
d.uuid.equals(AOSP_ACOUSTIC_ECHO_CANCELER)) { |
return true; |
@@ -128,7 +142,7 @@ class WebRtcAudioEffects { |
// AudioEffect.queryEffects() can throw IllegalStateException. |
@TargetApi(18) |
private static boolean isAutomaticGainControlExcludedByUUID() { |
- for (Descriptor d : AudioEffect.queryEffects()) { |
+ for (Descriptor d : getAvailableEffects()) { |
if (d.type.equals(AudioEffect.EFFECT_TYPE_AGC) && |
d.uuid.equals(AOSP_AUTOMATIC_GAIN_CONTROL)) { |
return true; |
@@ -141,7 +155,7 @@ class WebRtcAudioEffects { |
// AudioEffect.queryEffects() can throw IllegalStateException. |
@TargetApi(18) |
private static boolean isNoiseSuppressorExcludedByUUID() { |
- for (Descriptor d : AudioEffect.queryEffects()) { |
+ for (Descriptor d : getAvailableEffects()) { |
if (d.type.equals(AudioEffect.EFFECT_TYPE_NS) && |
d.uuid.equals(AOSP_NOISE_SUPPRESSOR)) { |
return true; |
@@ -150,6 +164,24 @@ class WebRtcAudioEffects { |
return false; |
} |
+ // Returns true if the device supports Acoustic Echo Cancellation (AEC). |
+ @TargetApi(18) |
+ private static boolean isAcousticEchoCancelerEffectAvailable() { |
+ return isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_AEC); |
+ } |
+ |
+ // Returns true if the device supports Automatic Gain Control (AGC). |
+ @TargetApi(18) |
+ private static boolean isAutomaticGainControlEffectAvailable() { |
+ return isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_AGC); |
+ } |
+ |
+ // Returns true if the device supports Noise Suppression (NS). |
+ @TargetApi(18) |
+ private static boolean isNoiseSuppressorEffectAvailable() { |
+ return isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_NS); |
+ } |
+ |
// Returns true if all conditions for supporting the HW AEC are fulfilled. |
// It will not be possible to enable the HW AEC if this method returns false. |
public static boolean canUseAcousticEchoCanceler() { |
@@ -378,4 +410,34 @@ class WebRtcAudioEffects { |
throw new AssertionError("Expected condition to be true"); |
} |
} |
+ |
+ // Returns the cached copy of the audio effects array, if available, or |
+ // queries the operating system for the list of effects. |
+ private static Descriptor[] getAvailableEffects() { |
+ if (cachedEffects != null) { |
+ return cachedEffects; |
+ } |
+ // The caching is best effort only - if this method is called from several |
+ // threads in parallel, they may end up doing the underlying OS call |
+ // multiple times. It's normally only called on one thread so there's no |
+ // real need to optimize for the multiple threads case. |
+ cachedEffects = AudioEffect.queryEffects(); |
+ return cachedEffects; |
+ } |
+ |
+ // Returns true if an effect of the specified type is available. Functionally |
+ // equivalent to (NoiseSuppressor|AutomaticGainControl|...).isAvailable(), but |
+ // faster as it avoids the expensive OS call to enumerate effects. |
+ private static boolean isEffectTypeAvailable(UUID effectType) { |
+ Descriptor[] effects = getAvailableEffects(); |
+ if (effects == null) { |
+ return false; |
+ } |
+ for (Descriptor d : effects) { |
+ if (d.type.equals(effectType)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
} |