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:
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user