Adds setAudio[Track/Record]StateCallback interfaces to the Java ADM

Bug: webrtc:10950
Change-Id: Ifa7bd7eb003bf97812ce0dfa5a0192ee8955419c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/151648
Reviewed-by: Alex Glaznev <glaznev@webrtc.org>
Commit-Queue: Henrik Andreassson <henrika@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29107}
This commit is contained in:
henrika
2019-09-06 13:45:11 +02:00
committed by Commit Bot
parent 81a08a7feb
commit 4d6b2691bd
4 changed files with 121 additions and 7 deletions

View File

@ -75,7 +75,9 @@ import org.webrtc.VideoTrack;
import org.webrtc.audio.AudioDeviceModule; import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule; import org.webrtc.audio.JavaAudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordErrorCallback; import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordErrorCallback;
import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordStateCallback;
import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackErrorCallback; import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackErrorCallback;
import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackStateCallback;
/** /**
* Peer connection client implementation. * Peer connection client implementation.
@ -501,12 +503,40 @@ public class PeerConnectionClient {
} }
}; };
// Set audio record state callbacks.
AudioRecordStateCallback audioRecordStateCallback = new AudioRecordStateCallback() {
@Override
public void onWebRtcAudioRecordStart() {
Log.i(TAG, "Audio recording starts");
}
@Override
public void onWebRtcAudioRecordStop() {
Log.i(TAG, "Audio recording stops");
}
};
// Set audio track state callbacks.
AudioTrackStateCallback audioTrackStateCallback = new AudioTrackStateCallback() {
@Override
public void onWebRtcAudioTrackStart() {
Log.i(TAG, "Audio playout starts");
}
@Override
public void onWebRtcAudioTrackStop() {
Log.i(TAG, "Audio playout stops");
}
};
return JavaAudioDeviceModule.builder(appContext) return JavaAudioDeviceModule.builder(appContext)
.setSamplesReadyCallback(saveRecordedAudioToFile) .setSamplesReadyCallback(saveRecordedAudioToFile)
.setUseHardwareAcousticEchoCanceler(!peerConnectionParameters.disableBuiltInAEC) .setUseHardwareAcousticEchoCanceler(!peerConnectionParameters.disableBuiltInAEC)
.setUseHardwareNoiseSuppressor(!peerConnectionParameters.disableBuiltInNS) .setUseHardwareNoiseSuppressor(!peerConnectionParameters.disableBuiltInNS)
.setAudioRecordErrorCallback(audioRecordErrorCallback) .setAudioRecordErrorCallback(audioRecordErrorCallback)
.setAudioTrackErrorCallback(audioTrackErrorCallback) .setAudioTrackErrorCallback(audioTrackErrorCallback)
.setAudioRecordStateCallback(audioRecordStateCallback)
.setAudioTrackStateCallback(audioTrackStateCallback)
.createAudioDeviceModule(); .createAudioDeviceModule();
} }

View File

@ -36,6 +36,8 @@ public class JavaAudioDeviceModule implements AudioDeviceModule {
private AudioTrackErrorCallback audioTrackErrorCallback; private AudioTrackErrorCallback audioTrackErrorCallback;
private AudioRecordErrorCallback audioRecordErrorCallback; private AudioRecordErrorCallback audioRecordErrorCallback;
private SamplesReadyCallback samplesReadyCallback; private SamplesReadyCallback samplesReadyCallback;
private AudioTrackStateCallback audioTrackStateCallback;
private AudioRecordStateCallback audioRecordStateCallback;
private boolean useHardwareAcousticEchoCanceler = isBuiltInAcousticEchoCancelerSupported(); private boolean useHardwareAcousticEchoCanceler = isBuiltInAcousticEchoCancelerSupported();
private boolean useHardwareNoiseSuppressor = isBuiltInNoiseSuppressorSupported(); private boolean useHardwareNoiseSuppressor = isBuiltInNoiseSuppressorSupported();
private boolean useStereoInput; private boolean useStereoInput;
@ -122,6 +124,22 @@ public class JavaAudioDeviceModule implements AudioDeviceModule {
return this; return this;
} }
/**
* Set a callback to retrieve information from the AudioTrack on when audio starts and stop.
*/
public Builder setAudioTrackStateCallback(AudioTrackStateCallback audioTrackStateCallback) {
this.audioTrackStateCallback = audioTrackStateCallback;
return this;
}
/**
* Set a callback to retrieve information from the AudioRecord on when audio starts and stops.
*/
public Builder setAudioRecordStateCallback(AudioRecordStateCallback audioRecordStateCallback) {
this.audioRecordStateCallback = audioRecordStateCallback;
return this;
}
/** /**
* Control if the built-in HW noise suppressor should be used or not. The default is on if it is * Control if the built-in HW noise suppressor should be used or not. The default is on if it is
* supported. It is possible to query support by calling isBuiltInNoiseSuppressorSupported(). * supported. It is possible to query support by calling isBuiltInNoiseSuppressorSupported().
@ -188,10 +206,10 @@ public class JavaAudioDeviceModule implements AudioDeviceModule {
Logging.d(TAG, "HW AEC will not be used."); Logging.d(TAG, "HW AEC will not be used.");
} }
final WebRtcAudioRecord audioInput = new WebRtcAudioRecord(context, audioManager, audioSource, final WebRtcAudioRecord audioInput = new WebRtcAudioRecord(context, audioManager, audioSource,
audioFormat, audioRecordErrorCallback, samplesReadyCallback, audioFormat, audioRecordErrorCallback, audioRecordStateCallback, samplesReadyCallback,
useHardwareAcousticEchoCanceler, useHardwareNoiseSuppressor); useHardwareAcousticEchoCanceler, useHardwareNoiseSuppressor);
final WebRtcAudioTrack audioOutput = final WebRtcAudioTrack audioOutput = new WebRtcAudioTrack(
new WebRtcAudioTrack(context, audioManager, audioTrackErrorCallback); context, audioManager, audioTrackErrorCallback, audioTrackStateCallback);
return new JavaAudioDeviceModule(context, audioManager, audioInput, audioOutput, return new JavaAudioDeviceModule(context, audioManager, audioInput, audioOutput,
inputSampleRate, outputSampleRate, useStereoInput, useStereoOutput); inputSampleRate, outputSampleRate, useStereoInput, useStereoOutput);
} }
@ -210,6 +228,12 @@ public class JavaAudioDeviceModule implements AudioDeviceModule {
void onWebRtcAudioRecordError(String errorMessage); void onWebRtcAudioRecordError(String errorMessage);
} }
/** Called when audio recording starts and stops. */
public static interface AudioRecordStateCallback {
void onWebRtcAudioRecordStart();
void onWebRtcAudioRecordStop();
}
/** /**
* Contains audio sample information. * Contains audio sample information.
*/ */
@ -265,6 +289,12 @@ public class JavaAudioDeviceModule implements AudioDeviceModule {
void onWebRtcAudioTrackError(String errorMessage); void onWebRtcAudioTrackError(String errorMessage);
} }
/** Called when audio playout starts and stops. */
public static interface AudioTrackStateCallback {
void onWebRtcAudioTrackStart();
void onWebRtcAudioTrackStop();
}
/** /**
* Returns true if the device supports built-in HW AEC, and the UUID is approved (some UUIDs can * Returns true if the device supports built-in HW AEC, and the UUID is approved (some UUIDs can
* be excluded). * be excluded).

View File

@ -28,6 +28,7 @@ import org.webrtc.Logging;
import org.webrtc.ThreadUtils; import org.webrtc.ThreadUtils;
import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordErrorCallback; import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordErrorCallback;
import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordStartErrorCode; import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordStartErrorCode;
import org.webrtc.audio.JavaAudioDeviceModule.AudioRecordStateCallback;
import org.webrtc.audio.JavaAudioDeviceModule.SamplesReadyCallback; import org.webrtc.audio.JavaAudioDeviceModule.SamplesReadyCallback;
class WebRtcAudioRecord { class WebRtcAudioRecord {
@ -54,6 +55,12 @@ class WebRtcAudioRecord {
// Guaranteed to be supported by all devices. // Guaranteed to be supported by all devices.
public static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; public static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
// Indicates AudioRecord has started recording audio.
private static final int AUDIO_RECORD_START = 0;
// Indicates AudioRecord has stopped recording audio.
private static final int AUDIO_RECORD_STOP = 1;
private final Context context; private final Context context;
private final AudioManager audioManager; private final AudioManager audioManager;
private final int audioSource; private final int audioSource;
@ -72,6 +79,7 @@ class WebRtcAudioRecord {
private byte[] emptyBytes; private byte[] emptyBytes;
private final @Nullable AudioRecordErrorCallback errorCallback; private final @Nullable AudioRecordErrorCallback errorCallback;
private final @Nullable AudioRecordStateCallback stateCallback;
private final @Nullable SamplesReadyCallback audioSamplesReadyCallback; private final @Nullable SamplesReadyCallback audioSamplesReadyCallback;
private final boolean isAcousticEchoCancelerSupported; private final boolean isAcousticEchoCancelerSupported;
private final boolean isNoiseSuppressorSupported; private final boolean isNoiseSuppressorSupported;
@ -95,6 +103,9 @@ class WebRtcAudioRecord {
Logging.d(TAG, "AudioRecordThread" + WebRtcAudioUtils.getThreadInfo()); Logging.d(TAG, "AudioRecordThread" + WebRtcAudioUtils.getThreadInfo());
assertTrue(audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING); assertTrue(audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING);
// Audio recording has started and the client is informed about it.
doAudioRecordStateCallback(AUDIO_RECORD_START);
long lastTime = System.nanoTime(); long lastTime = System.nanoTime();
while (keepAlive) { while (keepAlive) {
int bytesRead = audioRecord.read(byteBuffer, byteBuffer.capacity()); int bytesRead = audioRecord.read(byteBuffer, byteBuffer.capacity());
@ -131,6 +142,7 @@ class WebRtcAudioRecord {
try { try {
if (audioRecord != null) { if (audioRecord != null) {
audioRecord.stop(); audioRecord.stop();
doAudioRecordStateCallback(AUDIO_RECORD_STOP);
} }
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Logging.e(TAG, "AudioRecord.stop failed: " + e.getMessage()); Logging.e(TAG, "AudioRecord.stop failed: " + e.getMessage());
@ -148,13 +160,14 @@ class WebRtcAudioRecord {
@CalledByNative @CalledByNative
WebRtcAudioRecord(Context context, AudioManager audioManager) { WebRtcAudioRecord(Context context, AudioManager audioManager) {
this(context, audioManager, DEFAULT_AUDIO_SOURCE, DEFAULT_AUDIO_FORMAT, this(context, audioManager, DEFAULT_AUDIO_SOURCE, DEFAULT_AUDIO_FORMAT,
null /* errorCallback */, null /* audioSamplesReadyCallback */, null /* errorCallback */, null /* stateCallback */, null /* audioSamplesReadyCallback */,
WebRtcAudioEffects.isAcousticEchoCancelerSupported(), WebRtcAudioEffects.isAcousticEchoCancelerSupported(),
WebRtcAudioEffects.isNoiseSuppressorSupported()); WebRtcAudioEffects.isNoiseSuppressorSupported());
} }
public WebRtcAudioRecord(Context context, AudioManager audioManager, int audioSource, public WebRtcAudioRecord(Context context, AudioManager audioManager, int audioSource,
int audioFormat, @Nullable AudioRecordErrorCallback errorCallback, int audioFormat, @Nullable AudioRecordErrorCallback errorCallback,
@Nullable AudioRecordStateCallback stateCallback,
@Nullable SamplesReadyCallback audioSamplesReadyCallback, @Nullable SamplesReadyCallback audioSamplesReadyCallback,
boolean isAcousticEchoCancelerSupported, boolean isNoiseSuppressorSupported) { boolean isAcousticEchoCancelerSupported, boolean isNoiseSuppressorSupported) {
if (isAcousticEchoCancelerSupported && !WebRtcAudioEffects.isAcousticEchoCancelerSupported()) { if (isAcousticEchoCancelerSupported && !WebRtcAudioEffects.isAcousticEchoCancelerSupported()) {
@ -168,6 +181,7 @@ class WebRtcAudioRecord {
this.audioSource = audioSource; this.audioSource = audioSource;
this.audioFormat = audioFormat; this.audioFormat = audioFormat;
this.errorCallback = errorCallback; this.errorCallback = errorCallback;
this.stateCallback = stateCallback;
this.audioSamplesReadyCallback = audioSamplesReadyCallback; this.audioSamplesReadyCallback = audioSamplesReadyCallback;
this.isAcousticEchoCancelerSupported = isAcousticEchoCancelerSupported; this.isAcousticEchoCancelerSupported = isAcousticEchoCancelerSupported;
this.isNoiseSuppressorSupported = isNoiseSuppressorSupported; this.isNoiseSuppressorSupported = isNoiseSuppressorSupported;
@ -395,6 +409,19 @@ class WebRtcAudioRecord {
} }
} }
private void doAudioRecordStateCallback(int audioState) {
Logging.d(TAG, "doAudioRecordStateCallback: " + audioState);
if (stateCallback != null) {
if (audioState == WebRtcAudioRecord.AUDIO_RECORD_START) {
stateCallback.onWebRtcAudioRecordStart();
} else if (audioState == WebRtcAudioRecord.AUDIO_RECORD_STOP) {
stateCallback.onWebRtcAudioRecordStop();
} else {
Logging.e(TAG, "Invalid audio state");
}
}
}
// Reference from Android code, AudioFormat.getBytesPerSample. BitPerSample / 8 // Reference from Android code, AudioFormat.getBytesPerSample. BitPerSample / 8
// Default audio data format is PCM 16 bits per sample. // Default audio data format is PCM 16 bits per sample.
// Guaranteed to be supported by all devices // Guaranteed to be supported by all devices

View File

@ -26,6 +26,7 @@ import org.webrtc.Logging;
import org.webrtc.ThreadUtils; import org.webrtc.ThreadUtils;
import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackErrorCallback; import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackErrorCallback;
import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackStartErrorCode; import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackStartErrorCode;
import org.webrtc.audio.JavaAudioDeviceModule.AudioTrackStateCallback;
class WebRtcAudioTrack { class WebRtcAudioTrack {
private static final String TAG = "WebRtcAudioTrackExternal"; private static final String TAG = "WebRtcAudioTrackExternal";
@ -57,6 +58,12 @@ class WebRtcAudioTrack {
} }
} }
// Indicates the AudioTrack has started playing audio.
private static final int AUDIO_TRACK_START = 0;
// Indicates the AudioTrack has stopped playing audio.
private static final int AUDIO_TRACK_STOP = 1;
private long nativeAudioTrack; private long nativeAudioTrack;
private final Context context; private final Context context;
private final AudioManager audioManager; private final AudioManager audioManager;
@ -74,6 +81,7 @@ class WebRtcAudioTrack {
private byte[] emptyBytes; private byte[] emptyBytes;
private final @Nullable AudioTrackErrorCallback errorCallback; private final @Nullable AudioTrackErrorCallback errorCallback;
private final @Nullable AudioTrackStateCallback stateCallback;
/** /**
* Audio thread which keeps calling AudioTrack.write() to stream audio. * Audio thread which keeps calling AudioTrack.write() to stream audio.
@ -94,6 +102,9 @@ class WebRtcAudioTrack {
Logging.d(TAG, "AudioTrackThread" + WebRtcAudioUtils.getThreadInfo()); Logging.d(TAG, "AudioTrackThread" + WebRtcAudioUtils.getThreadInfo());
assertTrue(audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING); assertTrue(audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING);
// Audio playout has started and the client is informed about it.
doAudioTrackStateCallback(AUDIO_TRACK_START);
// Fixed size in bytes of each 10ms block of audio data that we ask for // Fixed size in bytes of each 10ms block of audio data that we ask for
// using callbacks to the native WebRTC client. // using callbacks to the native WebRTC client.
final int sizeInBytes = byteBuffer.capacity(); final int sizeInBytes = byteBuffer.capacity();
@ -140,6 +151,7 @@ class WebRtcAudioTrack {
try { try {
audioTrack.stop(); audioTrack.stop();
Logging.d(TAG, "AudioTrack.stop is done."); Logging.d(TAG, "AudioTrack.stop is done.");
doAudioTrackStateCallback(AUDIO_TRACK_STOP);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Logging.e(TAG, "AudioTrack.stop failed: " + e.getMessage()); Logging.e(TAG, "AudioTrack.stop failed: " + e.getMessage());
} }
@ -164,15 +176,17 @@ class WebRtcAudioTrack {
@CalledByNative @CalledByNative
WebRtcAudioTrack(Context context, AudioManager audioManager) { WebRtcAudioTrack(Context context, AudioManager audioManager) {
this(context, audioManager, null /* errorCallback */); this(context, audioManager, null /* errorCallback */, null /* stateCallback */);
} }
WebRtcAudioTrack( WebRtcAudioTrack(Context context, AudioManager audioManager,
Context context, AudioManager audioManager, @Nullable AudioTrackErrorCallback errorCallback) { @Nullable AudioTrackErrorCallback errorCallback,
@Nullable AudioTrackStateCallback stateCallback) {
threadChecker.detachThread(); threadChecker.detachThread();
this.context = context; this.context = context;
this.audioManager = audioManager; this.audioManager = audioManager;
this.errorCallback = errorCallback; this.errorCallback = errorCallback;
this.stateCallback = stateCallback;
this.volumeLogger = new VolumeLogger(audioManager); this.volumeLogger = new VolumeLogger(audioManager);
} }
@ -493,4 +507,17 @@ class WebRtcAudioTrack {
errorCallback.onWebRtcAudioTrackError(errorMessage); errorCallback.onWebRtcAudioTrackError(errorMessage);
} }
} }
private void doAudioTrackStateCallback(int audioState) {
Logging.d(TAG, "doAudioTrackStateCallback: " + audioState);
if (stateCallback != null) {
if (audioState == WebRtcAudioTrack.AUDIO_TRACK_START) {
stateCallback.onWebRtcAudioTrackStart();
} else if (audioState == WebRtcAudioTrack.AUDIO_TRACK_STOP) {
stateCallback.onWebRtcAudioTrackStop();
} else {
Logging.e(TAG, "Invalid audio state");
}
}
}
} }