Index: media/base/android/java/src/org/chromium/media/AudioRecordInput.java |
diff --git a/media/base/android/java/src/org/chromium/media/AudioRecordInput.java b/media/base/android/java/src/org/chromium/media/AudioRecordInput.java |
index b629f527bd4e4c26c0ac329b2cd5b70a2b0a7361..dec4a37d1480d56c7d07ac0fc5f834c086712969 100644 |
--- a/media/base/android/java/src/org/chromium/media/AudioRecordInput.java |
+++ b/media/base/android/java/src/org/chromium/media/AudioRecordInput.java |
@@ -7,6 +7,9 @@ package org.chromium.media; |
import android.media.AudioFormat; |
import android.media.AudioRecord; |
import android.media.MediaRecorder.AudioSource; |
+import android.media.audiofx.AcousticEchoCanceler; |
+import android.media.audiofx.AudioEffect; |
+import android.media.audiofx.AudioEffect.Descriptor; |
import android.os.Process; |
import android.util.Log; |
@@ -20,6 +23,8 @@ import java.nio.ByteBuffer; |
@JNINamespace("media") |
class AudioRecordInput { |
private static final String TAG = "AudioRecordInput"; |
+ // Set to true to enable debug logs. Always check in as false. |
+ private static final boolean DEBUG = false; |
// We are unable to obtain a precise measurement of the hardware delay on |
// Android. This is a conservative lower-bound based on measurments. It |
// could surely be tightened with further testing. |
@@ -30,9 +35,11 @@ class AudioRecordInput { |
private final int mChannels; |
private final int mBitsPerSample; |
private final int mHardwareDelayBytes; |
+ private final boolean mUsePlatformAEC; |
private ByteBuffer mBuffer; |
private AudioRecord mAudioRecord; |
private AudioRecordThread mAudioRecordThread; |
+ private AcousticEchoCanceler mAEC; |
private class AudioRecordThread extends Thread { |
// The "volatile" synchronization technique is discussed here: |
@@ -43,13 +50,7 @@ class AudioRecordInput { |
@Override |
public void run() { |
- try { |
- Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); |
- } catch (IllegalArgumentException e) { |
- Log.wtf(TAG, "setThreadPriority failed", e); |
- } catch (SecurityException e) { |
- Log.wtf(TAG, "setThreadPriority failed", e); |
- } |
+ Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); |
try { |
mAudioRecord.startRecording(); |
} catch (IllegalStateException e) { |
@@ -64,6 +65,11 @@ class AudioRecordInput { |
mHardwareDelayBytes); |
} else { |
Log.e(TAG, "read failed: " + bytesRead); |
+ if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) { |
+ // This can happen if there is already an active |
+ // AudioRecord (e.g. in another tab). |
+ mKeepAlive = false; |
+ } |
} |
} |
@@ -88,28 +94,26 @@ class AudioRecordInput { |
@CalledByNative |
private static AudioRecordInput createAudioRecordInput(long nativeAudioRecordInputStream, |
- int sampleRate, int channels, int bitsPerSample, int bytesPerBuffer) { |
+ int sampleRate, int channels, int bitsPerSample, int bytesPerBuffer, |
+ boolean usePlatformAEC) { |
return new AudioRecordInput(nativeAudioRecordInputStream, sampleRate, channels, |
- bitsPerSample, bytesPerBuffer); |
+ bitsPerSample, bytesPerBuffer, usePlatformAEC); |
} |
private AudioRecordInput(long nativeAudioRecordInputStream, int sampleRate, int channels, |
- int bitsPerSample, int bytesPerBuffer) { |
+ int bitsPerSample, int bytesPerBuffer, boolean usePlatformAEC) { |
mNativeAudioRecordInputStream = nativeAudioRecordInputStream; |
mSampleRate = sampleRate; |
mChannels = channels; |
mBitsPerSample = bitsPerSample; |
mHardwareDelayBytes = HARDWARE_DELAY_MS * sampleRate / 1000 * bitsPerSample / 8; |
+ mUsePlatformAEC = usePlatformAEC; |
// We use a direct buffer so that the native class can have access to |
// the underlying memory address. This avoids the need to copy from a |
// jbyteArray to native memory. More discussion of this here: |
// http://developer.android.com/training/articles/perf-jni.html |
- try { |
- mBuffer = ByteBuffer.allocateDirect(bytesPerBuffer); |
- } catch (IllegalArgumentException e) { |
- Log.wtf(TAG, "allocateDirect failure", e); |
- } |
+ mBuffer = ByteBuffer.allocateDirect(bytesPerBuffer); |
// Rather than passing the ByteBuffer with every OnData call (requiring |
// the potentially expensive GetDirectBufferAddress) we simply have the |
// the native class cache the address to the memory once. |
@@ -170,6 +174,26 @@ class AudioRecordInput { |
return false; |
} |
+ if (AcousticEchoCanceler.isAvailable()) { |
+ mAEC = AcousticEchoCanceler.create(mAudioRecord.getAudioSessionId()); |
+ if (mAEC == null) { |
+ Log.e(TAG, "AcousticEchoCanceler.create failed"); |
+ return false; |
+ } |
+ int ret = mAEC.setEnabled(mUsePlatformAEC); |
+ if (ret != AudioEffect.SUCCESS) { |
+ Log.e(TAG, "setEnabled error: " + ret); |
+ return false; |
+ } |
+ |
+ if (DEBUG) { |
+ Descriptor descriptor = mAEC.getDescriptor(); |
+ Log.d(TAG, "AcousticEchoCanceler " + |
+ "name: " + descriptor.name + ", " + |
+ "implementor: " + descriptor.implementor + ", " + |
+ "uuid: " + descriptor.uuid); |
+ } |
+ } |
return true; |
} |
@@ -207,6 +231,11 @@ class AudioRecordInput { |
// open() was not called. |
return; |
} |
+ |
+ if (mAEC != null) { |
+ mAEC.release(); |
+ mAEC = null; |
+ } |
mAudioRecord.release(); |
mAudioRecord = null; |
} |