Initial implementation of Android audio recording error handling.

BUG=b/34128648
R=henrika@webrtc.org

Review-Url: https://codereview.webrtc.org/2620453004 .
Cr-Commit-Position: refs/heads/master@{#16017}
This commit is contained in:
Alex Glaznev
2017-01-11 13:00:01 -08:00
parent 293e926362
commit 1a9e2108e3
2 changed files with 69 additions and 8 deletions

View File

@ -52,6 +52,8 @@ import org.webrtc.VideoRenderer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioRecord;
import org.webrtc.voiceengine.WebRtcAudioRecord.WebRtcAudioRecordErrorCallback;
import org.webrtc.voiceengine.WebRtcAudioUtils;
/**
@ -426,6 +428,27 @@ public class PeerConnectionClient {
WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(false);
}
// Set audio record error callbacks.
WebRtcAudioRecord.setErrorCallback(new WebRtcAudioRecordErrorCallback() {
@Override
public void onWebRtcAudioRecordInitError(String errorMessage) {
Log.e(TAG, "onWebRtcAudioRecordInitError: " + errorMessage);
reportError(errorMessage);
}
@Override
public void onWebRtcAudioRecordStartError(String errorMessage) {
Log.e(TAG, "onWebRtcAudioRecordStartError: " + errorMessage);
reportError(errorMessage);
}
@Override
public void onWebRtcAudioRecordError(String errorMessage) {
Log.e(TAG, "onWebRtcAudioRecordError: " + errorMessage);
reportError(errorMessage);
}
});
// Create peer connection factory.
if (!PeerConnectionFactory.initializeAndroidGlobals(
context, true, true, peerConnectionParameters.videoCodecHwAcceleration)) {

View File

@ -60,6 +60,20 @@ public class WebRtcAudioRecord {
private static volatile boolean microphoneMute = false;
private byte[] emptyBytes;
// Audio recording error handler functions.
public static interface WebRtcAudioRecordErrorCallback {
void onWebRtcAudioRecordInitError(String errorMessage);
void onWebRtcAudioRecordStartError(String errorMessage);
void onWebRtcAudioRecordError(String errorMessage);
}
private static WebRtcAudioRecordErrorCallback errorCallback = null;
public static void setErrorCallback(WebRtcAudioRecordErrorCallback errorCallback) {
Logging.d(TAG, "Set error callback");
WebRtcAudioRecord.errorCallback = errorCallback;
}
/**
* Audio thread which keeps calling ByteBuffer.read() waiting for audio
* to be recorded. Feeds recorded data to the native counterpart as a
@ -89,9 +103,11 @@ public class WebRtcAudioRecord {
}
nativeDataIsRecorded(bytesRead, nativeAudioRecord);
} else {
Logging.e(TAG, "AudioRecord.read failed: " + bytesRead);
String errorMessage = "AudioRecord.read failed: " + bytesRead;
Logging.e(TAG, errorMessage);
if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) {
keepAlive = false;
reportWebRtcAudioRecordError(errorMessage);
}
}
if (DEBUG) {
@ -150,11 +166,11 @@ public class WebRtcAudioRecord {
private int initRecording(int sampleRate, int channels) {
Logging.d(TAG, "initRecording(sampleRate=" + sampleRate + ", channels=" + channels + ")");
if (!WebRtcAudioUtils.hasPermission(context, android.Manifest.permission.RECORD_AUDIO)) {
Logging.e(TAG, "RECORD_AUDIO permission is missing");
reportWebRtcAudioRecordInitError("RECORD_AUDIO permission is missing");
return -1;
}
if (audioRecord != null) {
Logging.e(TAG, "InitRecording() called twice without StopRecording()");
reportWebRtcAudioRecordInitError("InitRecording called twice without StopRecording.");
return -1;
}
final int bytesPerFrame = channels * (BITS_PER_SAMPLE / 8);
@ -174,7 +190,7 @@ public class WebRtcAudioRecord {
int minBufferSize =
AudioRecord.getMinBufferSize(sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT);
if (minBufferSize == AudioRecord.ERROR || minBufferSize == AudioRecord.ERROR_BAD_VALUE) {
Logging.e(TAG, "AudioRecord.getMinBufferSize failed: " + minBufferSize);
reportWebRtcAudioRecordInitError("AudioRecord.getMinBufferSize failed: " + minBufferSize);
return -1;
}
Logging.d(TAG, "AudioRecord.getMinBufferSize: " + minBufferSize);
@ -188,12 +204,12 @@ public class WebRtcAudioRecord {
audioRecord = new AudioRecord(AudioSource.VOICE_COMMUNICATION, sampleRate, channelConfig,
AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes);
} catch (IllegalArgumentException e) {
Logging.e(TAG, e.getMessage());
reportWebRtcAudioRecordInitError("AudioRecord ctor error: " + e.getMessage());
releaseAudioResources();
return -1;
}
if (audioRecord == null || audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Logging.e(TAG, "Failed to create a new AudioRecord instance");
reportWebRtcAudioRecordInitError("Failed to create a new AudioRecord instance");
releaseAudioResources();
return -1;
}
@ -212,11 +228,12 @@ public class WebRtcAudioRecord {
try {
audioRecord.startRecording();
} catch (IllegalStateException e) {
Logging.e(TAG, "AudioRecord.startRecording failed: " + e.getMessage());
reportWebRtcAudioRecordStartError("AudioRecord.startRecording failed: " + e.getMessage());
return false;
}
if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
Logging.e(TAG, "AudioRecord.startRecording failed");
reportWebRtcAudioRecordStartError("AudioRecord.startRecording failed - incorrect state :"
+ audioRecord.getRecordingState());
return false;
}
audioThread = new AudioRecordThread("AudioRecordJavaThread");
@ -283,4 +300,25 @@ public class WebRtcAudioRecord {
audioRecord = null;
}
}
private void reportWebRtcAudioRecordInitError(String errorMessage) {
Logging.e(TAG, "Init recording error: " + errorMessage);
if (errorCallback != null) {
errorCallback.onWebRtcAudioRecordInitError(errorMessage);
}
}
private void reportWebRtcAudioRecordStartError(String errorMessage) {
Logging.e(TAG, "Start recording error: " + errorMessage);
if (errorCallback != null) {
errorCallback.onWebRtcAudioRecordStartError(errorMessage);
}
}
private void reportWebRtcAudioRecordError(String errorMessage) {
Logging.e(TAG, "Run-time recording error: " + errorMessage);
if (errorCallback != null) {
errorCallback.onWebRtcAudioRecordError(errorMessage);
}
}
}