Skip to content

Commit a52cc83

Browse files
authored
Merge pull request #63 from GetStream/add_audio_intercept
Add a way to intercept the audio samples before processing
2 parents b4c1f9b + fce4c0e commit a52cc83

File tree

4 files changed

+45
-2
lines changed

4 files changed

+45
-2
lines changed

stream-webrtc-android/api/stream-webrtc-android.api

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,6 +1974,10 @@ public abstract interface class org/webrtc/audio/AudioDeviceModule {
19741974
public abstract fun setSpeakerMute (Z)V
19751975
}
19761976

1977+
public abstract interface class org/webrtc/audio/AudioRecordDataCallback {
1978+
public abstract fun onAudioDataRecorded (IIILjava/nio/ByteBuffer;)V
1979+
}
1980+
19771981
public class org/webrtc/audio/JavaAudioDeviceModule : org/webrtc/audio/AudioDeviceModule {
19781982
public static fun builder (Landroid/content/Context;)Lorg/webrtc/audio/JavaAudioDeviceModule$Builder;
19791983
public fun getNativeAudioDeviceModulePointer ()J
@@ -2033,6 +2037,7 @@ public class org/webrtc/audio/JavaAudioDeviceModule$Builder {
20332037
public fun createAudioDeviceModule ()Lorg/webrtc/audio/JavaAudioDeviceModule;
20342038
public fun setAudioAttributes (Landroid/media/AudioAttributes;)Lorg/webrtc/audio/JavaAudioDeviceModule$Builder;
20352039
public fun setAudioFormat (I)Lorg/webrtc/audio/JavaAudioDeviceModule$Builder;
2040+
public fun setAudioRecordDataCallback (Lorg/webrtc/audio/AudioRecordDataCallback;)Lorg/webrtc/audio/JavaAudioDeviceModule$Builder;
20362041
public fun setAudioRecordErrorCallback (Lorg/webrtc/audio/JavaAudioDeviceModule$AudioRecordErrorCallback;)Lorg/webrtc/audio/JavaAudioDeviceModule$Builder;
20372042
public fun setAudioRecordStateCallback (Lorg/webrtc/audio/JavaAudioDeviceModule$AudioRecordStateCallback;)Lorg/webrtc/audio/JavaAudioDeviceModule$Builder;
20382043
public fun setAudioSource (I)Lorg/webrtc/audio/JavaAudioDeviceModule$Builder;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.webrtc.audio;
2+
3+
import androidx.annotation.NonNull;
4+
5+
import java.nio.ByteBuffer;
6+
7+
public interface AudioRecordDataCallback {
8+
/**
9+
* Invoked after an audio sample is recorded. Can be used to manipulate
10+
* the ByteBuffer before it's fed into WebRTC. Currently the audio in the
11+
* ByteBuffer is always PCM 16bit and the buffer sample size is ~10ms.
12+
*
13+
* @param audioFormat format in android.media.AudioFormat
14+
*/
15+
void onAudioDataRecorded(int audioFormat, int channelCount, int sampleRate, @NonNull ByteBuffer audioBuffer);
16+
}

stream-webrtc-android/src/main/java/org/webrtc/audio/JavaAudioDeviceModule.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public static class Builder {
5454
private AudioAttributes audioAttributes;
5555
private boolean useLowLatency;
5656
private boolean enableVolumeLogger;
57+
private AudioRecordDataCallback audioRecordDataCallback;
5758

5859
private Builder(Context context) {
5960
this.context = context;
@@ -226,6 +227,16 @@ public Builder setEnableVolumeLogger(boolean enableVolumeLogger) {
226227
return this;
227228
}
228229

230+
/**
231+
* Can be used to gain access to the raw ByteBuffer from the recording device before it's
232+
* fed into WebRTC. You can use this to manipulate the ByteBuffer (e.g. audio filters).
233+
* Make sure that the operation is fast.
234+
*/
235+
public Builder setAudioRecordDataCallback(AudioRecordDataCallback audioRecordDataCallback) {
236+
this.audioRecordDataCallback = audioRecordDataCallback;
237+
return this;
238+
}
239+
229240
/**
230241
* Construct an AudioDeviceModule based on the supplied arguments. The caller takes ownership
231242
* and is responsible for calling release().
@@ -260,7 +271,7 @@ public JavaAudioDeviceModule createAudioDeviceModule() {
260271
}
261272
final WebRtcAudioRecord audioInput = new WebRtcAudioRecord(context, executor, audioManager,
262273
audioSource, audioFormat, audioRecordErrorCallback, audioRecordStateCallback,
263-
samplesReadyCallback, useHardwareAcousticEchoCanceler, useHardwareNoiseSuppressor);
274+
samplesReadyCallback, audioRecordDataCallback, useHardwareAcousticEchoCanceler, useHardwareNoiseSuppressor);
264275
final WebRtcAudioTrack audioOutput =
265276
new WebRtcAudioTrack(context, audioManager, audioAttributes, audioTrackErrorCallback,
266277
audioTrackStateCallback, useLowLatency, enableVolumeLogger);

stream-webrtc-android/src/main/java/org/webrtc/audio/WebRtcAudioRecord.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class WebRtcAudioRecord {
104104

105105
private final @Nullable AudioRecordErrorCallback errorCallback;
106106
private final @Nullable AudioRecordStateCallback stateCallback;
107+
private final @Nullable AudioRecordDataCallback audioRecordDataCallback;
107108
private final @Nullable SamplesReadyCallback audioSamplesReadyCallback;
108109
private final boolean isAcousticEchoCancelerSupported;
109110
private final boolean isNoiseSuppressorSupported;
@@ -153,6 +154,13 @@ public void run() {
153154
captureTimeNs = audioTimestamp.nanoTime;
154155
}
155156
}
157+
158+
// Allow the client to intercept the ByteBuffer (to modify it)
159+
if (audioRecordDataCallback != null) {
160+
audioRecordDataCallback.onAudioDataRecorded(audioRecord.getAudioFormat(),
161+
audioRecord.getChannelCount(), audioRecord.getSampleRate(), byteBuffer);
162+
}
163+
156164
nativeDataIsRecorded(nativeAudioRecord, bytesRead, captureTimeNs);
157165
}
158166
if (audioSamplesReadyCallback != null) {
@@ -196,7 +204,8 @@ public void stopThread() {
196204
WebRtcAudioRecord(Context context, AudioManager audioManager) {
197205
this(context, newDefaultScheduler() /* scheduler */, audioManager, DEFAULT_AUDIO_SOURCE,
198206
DEFAULT_AUDIO_FORMAT, null /* errorCallback */, null /* stateCallback */,
199-
null /* audioSamplesReadyCallback */, WebRtcAudioEffects.isAcousticEchoCancelerSupported(),
207+
null /* audioSamplesReadyCallback */, /* audioRecordCallback */ null,
208+
WebRtcAudioEffects.isAcousticEchoCancelerSupported(),
200209
WebRtcAudioEffects.isNoiseSuppressorSupported());
201210
}
202211

@@ -205,6 +214,7 @@ public WebRtcAudioRecord(Context context, ScheduledExecutorService scheduler,
205214
@Nullable AudioRecordErrorCallback errorCallback,
206215
@Nullable AudioRecordStateCallback stateCallback,
207216
@Nullable SamplesReadyCallback audioSamplesReadyCallback,
217+
@Nullable AudioRecordDataCallback audioRecordDataCallback,
208218
boolean isAcousticEchoCancelerSupported, boolean isNoiseSuppressorSupported) {
209219
if (isAcousticEchoCancelerSupported && !WebRtcAudioEffects.isAcousticEchoCancelerSupported()) {
210220
throw new IllegalArgumentException("HW AEC not supported");
@@ -219,6 +229,7 @@ public WebRtcAudioRecord(Context context, ScheduledExecutorService scheduler,
219229
this.audioFormat = audioFormat;
220230
this.errorCallback = errorCallback;
221231
this.stateCallback = stateCallback;
232+
this.audioRecordDataCallback = audioRecordDataCallback;
222233
this.audioSamplesReadyCallback = audioSamplesReadyCallback;
223234
this.isAcousticEchoCancelerSupported = isAcousticEchoCancelerSupported;
224235
this.isNoiseSuppressorSupported = isNoiseSuppressorSupported;

0 commit comments

Comments
 (0)