Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: media/base/android/java/src/org/chromium/media/AudioRecordInput.java

Issue 115413002: Enable platform echo cancellation through the AudioRecord path. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.media; 5 package org.chromium.media;
6 6
7 import android.media.AudioFormat; 7 import android.media.AudioFormat;
8 import android.media.AudioRecord; 8 import android.media.AudioRecord;
9 import android.media.MediaRecorder.AudioSource; 9 import android.media.MediaRecorder.AudioSource;
10 import android.media.audiofx.AcousticEchoCanceler;
11 import android.media.audiofx.AudioEffect;
12 import android.media.audiofx.AudioEffect.Descriptor;
10 import android.os.Process; 13 import android.os.Process;
11 import android.util.Log; 14 import android.util.Log;
12 15
13 import org.chromium.base.CalledByNative; 16 import org.chromium.base.CalledByNative;
14 import org.chromium.base.JNINamespace; 17 import org.chromium.base.JNINamespace;
15 18
16 import java.nio.ByteBuffer; 19 import java.nio.ByteBuffer;
17 20
18 // Owned by its native counterpart declared in audio_record_input.h. Refer to 21 // Owned by its native counterpart declared in audio_record_input.h. Refer to
19 // that class for general comments. 22 // that class for general comments.
20 @JNINamespace("media") 23 @JNINamespace("media")
21 class AudioRecordInput { 24 class AudioRecordInput {
22 private static final String TAG = "AudioRecordInput"; 25 private static final String TAG = "AudioRecordInput";
26 // Set to true to enable debug logs. Always check in as false.
27 private static final boolean DEBUG = false;
23 // We are unable to obtain a precise measurement of the hardware delay on 28 // We are unable to obtain a precise measurement of the hardware delay on
24 // Android. This is a conservative lower-bound based on measurments. It 29 // Android. This is a conservative lower-bound based on measurments. It
25 // could surely be tightened with further testing. 30 // could surely be tightened with further testing.
26 private static final int HARDWARE_DELAY_MS = 100; 31 private static final int HARDWARE_DELAY_MS = 100;
27 32
28 private final long mNativeAudioRecordInputStream; 33 private final long mNativeAudioRecordInputStream;
29 private final int mSampleRate; 34 private final int mSampleRate;
30 private final int mChannels; 35 private final int mChannels;
31 private final int mBitsPerSample; 36 private final int mBitsPerSample;
32 private final int mHardwareDelayBytes; 37 private final int mHardwareDelayBytes;
38 private final boolean mUsePlatformAEC;
33 private ByteBuffer mBuffer; 39 private ByteBuffer mBuffer;
34 private AudioRecord mAudioRecord; 40 private AudioRecord mAudioRecord;
35 private AudioRecordThread mAudioRecordThread; 41 private AudioRecordThread mAudioRecordThread;
42 private AcousticEchoCanceler mAEC;
36 43
37 private class AudioRecordThread extends Thread { 44 private class AudioRecordThread extends Thread {
38 // The "volatile" synchronization technique is discussed here: 45 // The "volatile" synchronization technique is discussed here:
39 // http://stackoverflow.com/a/106787/299268 46 // http://stackoverflow.com/a/106787/299268
40 // and more generally in this article: 47 // and more generally in this article:
41 // https://www.ibm.com/developerworks/java/library/j-jtp06197/ 48 // https://www.ibm.com/developerworks/java/library/j-jtp06197/
42 private volatile boolean mKeepAlive = true; 49 private volatile boolean mKeepAlive = true;
43 50
44 @Override 51 @Override
45 public void run() { 52 public void run() {
46 try { 53 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
47 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
48 } catch (IllegalArgumentException e) {
49 Log.wtf(TAG, "setThreadPriority failed", e);
50 } catch (SecurityException e) {
51 Log.wtf(TAG, "setThreadPriority failed", e);
52 }
53 try { 54 try {
54 mAudioRecord.startRecording(); 55 mAudioRecord.startRecording();
55 } catch (IllegalStateException e) { 56 } catch (IllegalStateException e) {
56 Log.e(TAG, "startRecording failed", e); 57 Log.e(TAG, "startRecording failed", e);
57 return; 58 return;
58 } 59 }
59 60
60 while (mKeepAlive) { 61 while (mKeepAlive) {
61 int bytesRead = mAudioRecord.read(mBuffer, mBuffer.capacity()); 62 int bytesRead = mAudioRecord.read(mBuffer, mBuffer.capacity());
62 if (bytesRead > 0) { 63 if (bytesRead > 0) {
63 nativeOnData(mNativeAudioRecordInputStream, bytesRead, 64 nativeOnData(mNativeAudioRecordInputStream, bytesRead,
64 mHardwareDelayBytes); 65 mHardwareDelayBytes);
65 } else { 66 } else {
66 Log.e(TAG, "read failed: " + bytesRead); 67 Log.e(TAG, "read failed: " + bytesRead);
68 if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) {
69 // This can happen if there is already an active
70 // AudioRecord (e.g. in another tab).
71 mKeepAlive = false;
72 }
67 } 73 }
68 } 74 }
69 75
70 try { 76 try {
71 mAudioRecord.stop(); 77 mAudioRecord.stop();
72 } catch (IllegalStateException e) { 78 } catch (IllegalStateException e) {
73 Log.e(TAG, "stop failed", e); 79 Log.e(TAG, "stop failed", e);
74 } 80 }
75 } 81 }
76 82
77 public void joinRecordThread() { 83 public void joinRecordThread() {
78 mKeepAlive = false; 84 mKeepAlive = false;
79 while (isAlive()) { 85 while (isAlive()) {
80 try { 86 try {
81 join(); 87 join();
82 } catch (InterruptedException e) { 88 } catch (InterruptedException e) {
83 // Ignore. 89 // Ignore.
84 } 90 }
85 } 91 }
86 } 92 }
87 } 93 }
88 94
89 @CalledByNative 95 @CalledByNative
90 private static AudioRecordInput createAudioRecordInput(long nativeAudioRecor dInputStream, 96 private static AudioRecordInput createAudioRecordInput(long nativeAudioRecor dInputStream,
91 int sampleRate, int channels, int bitsPerSample, int bytesPerBuffer) { 97 int sampleRate, int channels, int bitsPerSample, int bytesPerBuffer,
98 boolean usePlatformAEC) {
92 return new AudioRecordInput(nativeAudioRecordInputStream, sampleRate, ch annels, 99 return new AudioRecordInput(nativeAudioRecordInputStream, sampleRate, ch annels,
93 bitsPerSample, bytesPerBuffer); 100 bitsPerSample, bytesPerBuffer, usePlatformAE C);
94 } 101 }
95 102
96 private AudioRecordInput(long nativeAudioRecordInputStream, int sampleRate, int channels, 103 private AudioRecordInput(long nativeAudioRecordInputStream, int sampleRate, int channels,
97 int bitsPerSample, int bytesPerBuffer) { 104 int bitsPerSample, int bytesPerBuffer, boolean useP latformAEC) {
98 mNativeAudioRecordInputStream = nativeAudioRecordInputStream; 105 mNativeAudioRecordInputStream = nativeAudioRecordInputStream;
99 mSampleRate = sampleRate; 106 mSampleRate = sampleRate;
100 mChannels = channels; 107 mChannels = channels;
101 mBitsPerSample = bitsPerSample; 108 mBitsPerSample = bitsPerSample;
102 mHardwareDelayBytes = HARDWARE_DELAY_MS * sampleRate / 1000 * bitsPerSam ple / 8; 109 mHardwareDelayBytes = HARDWARE_DELAY_MS * sampleRate / 1000 * bitsPerSam ple / 8;
110 mUsePlatformAEC = usePlatformAEC;
103 111
104 // We use a direct buffer so that the native class can have access to 112 // We use a direct buffer so that the native class can have access to
105 // the underlying memory address. This avoids the need to copy from a 113 // the underlying memory address. This avoids the need to copy from a
106 // jbyteArray to native memory. More discussion of this here: 114 // jbyteArray to native memory. More discussion of this here:
107 // http://developer.android.com/training/articles/perf-jni.html 115 // http://developer.android.com/training/articles/perf-jni.html
108 try { 116 mBuffer = ByteBuffer.allocateDirect(bytesPerBuffer);
109 mBuffer = ByteBuffer.allocateDirect(bytesPerBuffer);
110 } catch (IllegalArgumentException e) {
111 Log.wtf(TAG, "allocateDirect failure", e);
112 }
113 // Rather than passing the ByteBuffer with every OnData call (requiring 117 // Rather than passing the ByteBuffer with every OnData call (requiring
114 // the potentially expensive GetDirectBufferAddress) we simply have the 118 // the potentially expensive GetDirectBufferAddress) we simply have the
115 // the native class cache the address to the memory once. 119 // the native class cache the address to the memory once.
116 // 120 //
117 // Unfortunately, profiling with traceview was unable to either confirm 121 // Unfortunately, profiling with traceview was unable to either confirm
118 // or deny the advantage of this approach, as the values for 122 // or deny the advantage of this approach, as the values for
119 // nativeOnData() were not stable across runs. 123 // nativeOnData() were not stable across runs.
120 nativeCacheDirectBufferAddress(mNativeAudioRecordInputStream, mBuffer); 124 nativeCacheDirectBufferAddress(mNativeAudioRecordInputStream, mBuffer);
121 } 125 }
122 126
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 mAudioRecord = new AudioRecord(AudioSource.VOICE_COMMUNICATION, 167 mAudioRecord = new AudioRecord(AudioSource.VOICE_COMMUNICATION,
164 mSampleRate, 168 mSampleRate,
165 channelConfig, 169 channelConfig,
166 audioFormat, 170 audioFormat,
167 audioRecordBufferSizeInBytes); 171 audioRecordBufferSizeInBytes);
168 } catch (IllegalArgumentException e) { 172 } catch (IllegalArgumentException e) {
169 Log.e(TAG, "AudioRecord failed", e); 173 Log.e(TAG, "AudioRecord failed", e);
170 return false; 174 return false;
171 } 175 }
172 176
177 if (AcousticEchoCanceler.isAvailable()) {
178 mAEC = AcousticEchoCanceler.create(mAudioRecord.getAudioSessionId()) ;
179 if (mAEC == null) {
180 Log.e(TAG, "AcousticEchoCanceler.create failed");
181 return false;
182 }
183 int ret = mAEC.setEnabled(mUsePlatformAEC);
184 if (ret != AudioEffect.SUCCESS) {
185 Log.e(TAG, "setEnabled error: " + ret);
186 return false;
187 }
188
189 if (DEBUG) {
190 Descriptor descriptor = mAEC.getDescriptor();
191 Log.d(TAG, "AcousticEchoCanceler " +
192 "name: " + descriptor.name + ", " +
193 "implementor: " + descriptor.implementor + ", " +
194 "uuid: " + descriptor.uuid);
195 }
196 }
173 return true; 197 return true;
174 } 198 }
175 199
176 @CalledByNative 200 @CalledByNative
177 private void start() { 201 private void start() {
178 if (mAudioRecord == null) { 202 if (mAudioRecord == null) {
179 Log.e(TAG, "start() called before open()."); 203 Log.e(TAG, "start() called before open().");
180 return; 204 return;
181 } 205 }
182 if (mAudioRecordThread != null) { 206 if (mAudioRecordThread != null) {
(...skipping 17 matching lines...) Expand all
200 @CalledByNative 224 @CalledByNative
201 private void close() { 225 private void close() {
202 if (mAudioRecordThread != null) { 226 if (mAudioRecordThread != null) {
203 Log.e(TAG, "close() called before stop()."); 227 Log.e(TAG, "close() called before stop().");
204 return; 228 return;
205 } 229 }
206 if (mAudioRecord == null) { 230 if (mAudioRecord == null) {
207 // open() was not called. 231 // open() was not called.
208 return; 232 return;
209 } 233 }
234
235 if (mAEC != null) {
236 mAEC.release();
237 mAEC = null;
238 }
210 mAudioRecord.release(); 239 mAudioRecord.release();
211 mAudioRecord = null; 240 mAudioRecord = null;
212 } 241 }
213 242
214 private native void nativeCacheDirectBufferAddress(long nativeAudioRecordInp utStream, 243 private native void nativeCacheDirectBufferAddress(long nativeAudioRecordInp utStream,
215 ByteBuffer buffer); 244 ByteBuffer buffer);
216 private native void nativeOnData(long nativeAudioRecordInputStream, int size , 245 private native void nativeOnData(long nativeAudioRecordInputStream, int size ,
217 int hardwareDelayBytes); 246 int hardwareDelayBytes);
218 } 247 }
OLDNEW
« no previous file with comments | « media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java ('k') | media/base/channel_mixer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698