From 1a9e2108e3197858b3f31c5380c3a3071c29f62e Mon Sep 17 00:00:00 2001 From: Alex Glaznev Date: Wed, 11 Jan 2017 13:00:01 -0800 Subject: [PATCH] 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} --- .../appspot/apprtc/PeerConnectionClient.java | 23 ++++++++ .../webrtc/voiceengine/WebRtcAudioRecord.java | 54 ++++++++++++++++--- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java index cc5e457cbf..406b4a2f96 100644 --- a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java @@ -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)) { diff --git a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java index 23b2c60c9c..a9bade2cac 100644 --- a/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java +++ b/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java @@ -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); + } + } }