Optimize the repeated calls to AudioEffect.queryEffects() on Android
This CL eliminates repeated calls to AudioEffect.queryEffects() on Android when configuring the audio device. Each of these calls was taking 5-10 milliseconds on the devices I was testing (Nexus 4, Nexus 5), and setting up the audio device involved around 10 of these calls. This change adds a method that checks the cached list of effects before calling the underlying operating system API; this eliminated about half of these calls. The other half happened inside static methods such as NoiseSuppressor.isAvailable(), which are just convenience wrappers for searching through the list of effects. These calls have been replaced with searching through the cached list of effects, reducing the time to configure audio processing effects from 60-80 ms to 5-10. This results in a similar improvement in call setup time. BUG= Review-Url: https://codereview.webrtc.org/2051323002 Cr-Commit-Position: refs/heads/master@{#13115}
This commit is contained in:
parent
63797930be
commit
880ffeb6c0
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user