Initial implementation of Android audio playback error handling.

BUG=b/34128648

Review-Url: https://codereview.webrtc.org/2861573002
Cr-Commit-Position: refs/heads/master@{#18002}
This commit is contained in:
glaznev
2017-05-03 11:32:39 -07:00
committed by Commit bot
parent 41102775da
commit 49c8f26ad1
2 changed files with 61 additions and 6 deletions

View File

@ -56,8 +56,10 @@ import org.webrtc.VideoSource;
import org.webrtc.VideoTrack; import org.webrtc.VideoTrack;
import org.webrtc.voiceengine.WebRtcAudioManager; import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioRecord; import org.webrtc.voiceengine.WebRtcAudioRecord;
import org.webrtc.voiceengine.WebRtcAudioTrack;
import org.webrtc.voiceengine.WebRtcAudioRecord.AudioRecordStartErrorCode; import org.webrtc.voiceengine.WebRtcAudioRecord.AudioRecordStartErrorCode;
import org.webrtc.voiceengine.WebRtcAudioRecord.WebRtcAudioRecordErrorCallback; import org.webrtc.voiceengine.WebRtcAudioRecord.WebRtcAudioRecordErrorCallback;
import org.webrtc.voiceengine.WebRtcAudioTrack.WebRtcAudioTrackErrorCallback;
import org.webrtc.voiceengine.WebRtcAudioUtils; import org.webrtc.voiceengine.WebRtcAudioUtils;
/** /**
@ -473,6 +475,23 @@ public class PeerConnectionClient {
} }
}); });
WebRtcAudioTrack.setErrorCallback(new WebRtcAudioTrackErrorCallback() {
@Override
public void onWebRtcAudioTrackInitError(String errorMessage) {
reportError(errorMessage);
}
@Override
public void onWebRtcAudioTrackStartError(String errorMessage) {
reportError(errorMessage);
}
@Override
public void onWebRtcAudioTrackError(String errorMessage) {
reportError(errorMessage);
}
});
// Create peer connection factory. // Create peer connection factory.
PeerConnectionFactory.initializeAndroidGlobals( PeerConnectionFactory.initializeAndroidGlobals(
context, peerConnectionParameters.videoCodecHwAcceleration); context, peerConnectionParameters.videoCodecHwAcceleration);

View File

@ -50,6 +50,19 @@ public class WebRtcAudioTrack {
private static volatile boolean speakerMute = false; private static volatile boolean speakerMute = false;
private byte[] emptyBytes; private byte[] emptyBytes;
public static interface WebRtcAudioTrackErrorCallback {
void onWebRtcAudioTrackInitError(String errorMessage);
void onWebRtcAudioTrackStartError(String errorMessage);
void onWebRtcAudioTrackError(String errorMessage);
}
private static WebRtcAudioTrackErrorCallback errorCallback = null;
public static void setErrorCallback(WebRtcAudioTrackErrorCallback errorCallback) {
Logging.d(TAG, "Set error callback");
WebRtcAudioTrack.errorCallback = errorCallback;
}
/** /**
* Audio thread which keeps calling AudioTrack.write() to stream audio. * Audio thread which keeps calling AudioTrack.write() to stream audio.
* Data is periodically acquired from the native WebRTC layer using the * Data is periodically acquired from the native WebRTC layer using the
@ -76,7 +89,7 @@ public class WebRtcAudioTrack {
audioTrack.play(); audioTrack.play();
assertTrue(audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING); assertTrue(audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Logging.e(TAG, "AudioTrack.play failed: " + e.getMessage()); reportWebRtcAudioTrackStartError("AudioTrack.play failed: " + e.getMessage());
releaseAudioResources(); releaseAudioResources();
return; return;
} }
@ -109,6 +122,7 @@ public class WebRtcAudioTrack {
Logging.e(TAG, "AudioTrack.write failed: " + bytesWritten); Logging.e(TAG, "AudioTrack.write failed: " + bytesWritten);
if (bytesWritten == AudioTrack.ERROR_INVALID_OPERATION) { if (bytesWritten == AudioTrack.ERROR_INVALID_OPERATION) {
keepAlive = false; keepAlive = false;
reportWebRtcAudioTrackError("AudioTrack.write failed: " + bytesWritten);
} }
} }
// The byte buffer must be rewinded since byteBuffer.position() is // The byte buffer must be rewinded since byteBuffer.position() is
@ -186,14 +200,14 @@ public class WebRtcAudioTrack {
// reports of "getMinBufferSize(): error querying hardware". Hence, it // reports of "getMinBufferSize(): error querying hardware". Hence, it
// can happen that |minBufferSizeInBytes| contains an invalid value. // can happen that |minBufferSizeInBytes| contains an invalid value.
if (minBufferSizeInBytes < byteBuffer.capacity()) { if (minBufferSizeInBytes < byteBuffer.capacity()) {
Logging.e(TAG, "AudioTrack.getMinBufferSize returns an invalid value."); reportWebRtcAudioTrackInitError("AudioTrack.getMinBufferSize returns an invalid value.");
return false; return false;
} }
// Ensure that prevision audio session was stopped correctly before trying // Ensure that prevision audio session was stopped correctly before trying
// to create a new AudioTrack. // to create a new AudioTrack.
if (audioTrack != null) { if (audioTrack != null) {
Logging.e(TAG, "Conflict with existing AudioTrack."); reportWebRtcAudioTrackInitError("Conflict with existing AudioTrack.");
return false; return false;
} }
try { try {
@ -215,7 +229,7 @@ public class WebRtcAudioTrack {
AudioFormat.ENCODING_PCM_16BIT, minBufferSizeInBytes, AudioTrack.MODE_STREAM); AudioFormat.ENCODING_PCM_16BIT, minBufferSizeInBytes, AudioTrack.MODE_STREAM);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Logging.d(TAG, e.getMessage()); reportWebRtcAudioTrackInitError(e.getMessage());
releaseAudioResources(); releaseAudioResources();
return false; return false;
} }
@ -224,7 +238,7 @@ public class WebRtcAudioTrack {
// initialized upon creation. Seems to be the case e.g. when the maximum // initialized upon creation. Seems to be the case e.g. when the maximum
// number of globally available audio tracks is exceeded. // number of globally available audio tracks is exceeded.
if (audioTrack == null || audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { if (audioTrack == null || audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Logging.e(TAG, "Initialization of audio track failed."); reportWebRtcAudioTrackInitError("Initialization of audio track failed.");
releaseAudioResources(); releaseAudioResources();
return false; return false;
} }
@ -238,7 +252,7 @@ public class WebRtcAudioTrack {
assertTrue(audioTrack != null); assertTrue(audioTrack != null);
assertTrue(audioThread == null); assertTrue(audioThread == null);
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Logging.e(TAG, "AudioTrack instance is not successfully initialized."); reportWebRtcAudioTrackStartError("AudioTrack instance is not successfully initialized.");
return false; return false;
} }
audioThread = new AudioTrackThread("AudioTrackJavaThread"); audioThread = new AudioTrackThread("AudioTrackJavaThread");
@ -384,4 +398,26 @@ public class WebRtcAudioTrack {
audioTrack = null; audioTrack = null;
} }
} }
private void reportWebRtcAudioTrackInitError(String errorMessage) {
Logging.e(TAG, "Init error: " + errorMessage);
if (errorCallback != null) {
errorCallback.onWebRtcAudioTrackInitError(errorMessage);
}
}
private void reportWebRtcAudioTrackStartError(String errorMessage) {
Logging.e(TAG, "Start error: " + errorMessage);
if (errorCallback != null) {
errorCallback.onWebRtcAudioTrackStartError(errorMessage);
}
}
private void reportWebRtcAudioTrackError(String errorMessage) {
Logging.e(TAG, "Run-time playback error: " + errorMessage);
if (errorCallback != null) {
errorCallback.onWebRtcAudioTrackError(errorMessage);
}
}
} }