Adds extended audio state logs to Android audio.
NOTRY=TRUE Bug: webrtc:8583 Change-Id: I2e9cb9354cc77c597a308b1f6c519c015a263842 Reviewed-on: https://webrtc-review.googlesource.com/25826 Commit-Queue: Henrik Andreassson <henrika@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20934}
This commit is contained in:
@ -89,11 +89,6 @@ public class WebRtcAudioManager {
|
||||
|
||||
private static final int DEFAULT_FRAME_PER_BUFFER = 256;
|
||||
|
||||
// List of possible audio modes.
|
||||
private static final String[] AUDIO_MODES = new String[] {
|
||||
"MODE_NORMAL", "MODE_RINGTONE", "MODE_IN_CALL", "MODE_IN_COMMUNICATION",
|
||||
};
|
||||
|
||||
// Private utility class that periodically checks and logs the volume level
|
||||
// of the audio stream that is currently controlled by the volume control.
|
||||
// A timer triggers logs once every 30 seconds and the timer's associated
|
||||
@ -189,7 +184,8 @@ public class WebRtcAudioManager {
|
||||
if (initialized) {
|
||||
return true;
|
||||
}
|
||||
Logging.d(TAG, "audio mode is: " + AUDIO_MODES[audioManager.getMode()]);
|
||||
Logging.d(TAG, "audio mode is: "
|
||||
+ WebRtcAudioUtils.modeToString(audioManager.getMode()));
|
||||
initialized = true;
|
||||
volumeLogger.start();
|
||||
return true;
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
package org.webrtc.voiceengine;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder.AudioSource;
|
||||
@ -19,7 +18,6 @@ import android.os.Process;
|
||||
import java.lang.System;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.webrtc.ContextUtils;
|
||||
import org.webrtc.Logging;
|
||||
import org.webrtc.ThreadUtils;
|
||||
|
||||
@ -252,6 +250,7 @@ public class WebRtcAudioRecord {
|
||||
audioThread.stopThread();
|
||||
if (!ThreadUtils.joinUninterruptibly(audioThread, AUDIO_RECORD_THREAD_JOIN_TIMEOUT_MS)) {
|
||||
Logging.e(TAG, "Join of AudioRecordJavaThread timed out");
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
}
|
||||
audioThread = null;
|
||||
if (effects != null) {
|
||||
@ -320,6 +319,7 @@ public class WebRtcAudioRecord {
|
||||
|
||||
private void reportWebRtcAudioRecordInitError(String errorMessage) {
|
||||
Logging.e(TAG, "Init recording error: " + errorMessage);
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
if (errorCallback != null) {
|
||||
errorCallback.onWebRtcAudioRecordInitError(errorMessage);
|
||||
}
|
||||
@ -328,6 +328,7 @@ public class WebRtcAudioRecord {
|
||||
private void reportWebRtcAudioRecordStartError(
|
||||
AudioRecordStartErrorCode errorCode, String errorMessage) {
|
||||
Logging.e(TAG, "Start recording error: " + errorCode + ". " + errorMessage);
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
if (errorCallback != null) {
|
||||
errorCallback.onWebRtcAudioRecordStartError(errorCode, errorMessage);
|
||||
}
|
||||
@ -335,6 +336,7 @@ public class WebRtcAudioRecord {
|
||||
|
||||
private void reportWebRtcAudioRecordError(String errorMessage) {
|
||||
Logging.e(TAG, "Run-time recording error: " + errorMessage);
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
if (errorCallback != null) {
|
||||
errorCallback.onWebRtcAudioRecordError(errorMessage);
|
||||
}
|
||||
|
||||
@ -339,6 +339,7 @@ public class WebRtcAudioTrack {
|
||||
audioThread.interrupt();
|
||||
if (!ThreadUtils.joinUninterruptibly(audioThread, AUDIO_TRACK_THREAD_JOIN_TIMEOUT_MS)) {
|
||||
Logging.e(TAG, "Join of AudioTrackThread timed out.");
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
}
|
||||
Logging.d(TAG, "AudioTrackThread has now been stopped.");
|
||||
audioThread = null;
|
||||
@ -491,6 +492,7 @@ public class WebRtcAudioTrack {
|
||||
|
||||
private void reportWebRtcAudioTrackInitError(String errorMessage) {
|
||||
Logging.e(TAG, "Init playout error: " + errorMessage);
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
if (errorCallback != null) {
|
||||
errorCallbackOld.onWebRtcAudioTrackInitError(errorMessage);
|
||||
}
|
||||
@ -502,6 +504,7 @@ public class WebRtcAudioTrack {
|
||||
private void reportWebRtcAudioTrackStartError(
|
||||
AudioTrackStartErrorCode errorCode, String errorMessage) {
|
||||
Logging.e(TAG, "Start playout error: " + errorCode + ". " + errorMessage);
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
if (errorCallback != null) {
|
||||
errorCallbackOld.onWebRtcAudioTrackStartError(errorMessage);
|
||||
}
|
||||
@ -512,6 +515,7 @@ public class WebRtcAudioTrack {
|
||||
|
||||
private void reportWebRtcAudioTrackError(String errorMessage) {
|
||||
Logging.e(TAG, "Run-time playback error: " + errorMessage);
|
||||
WebRtcAudioUtils.logAudioState(TAG);
|
||||
if (errorCallback != null) {
|
||||
errorCallbackOld.onWebRtcAudioTrackError(errorMessage);
|
||||
}
|
||||
|
||||
@ -10,13 +10,25 @@
|
||||
|
||||
package org.webrtc.voiceengine;
|
||||
|
||||
import static android.media.AudioManager.MODE_IN_CALL;
|
||||
import static android.media.AudioManager.MODE_IN_COMMUNICATION;
|
||||
import static android.media.AudioManager.MODE_NORMAL;
|
||||
import static android.media.AudioManager.MODE_RINGTONE;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecordingConfiguration;
|
||||
import android.media.MediaRecorder.AudioSource;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import java.lang.Thread;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.webrtc.ContextUtils;
|
||||
import org.webrtc.Logging;
|
||||
|
||||
public final class WebRtcAudioUtils {
|
||||
@ -196,7 +208,7 @@ public final class WebRtcAudioUtils {
|
||||
}
|
||||
|
||||
// Information about the current build, taken from system properties.
|
||||
public static void logDeviceInfo(String tag) {
|
||||
static void logDeviceInfo(String tag) {
|
||||
Logging.d(tag, "Android SDK: " + Build.VERSION.SDK_INT + ", "
|
||||
+ "Release: " + Build.VERSION.RELEASE + ", "
|
||||
+ "Brand: " + Build.BRAND + ", "
|
||||
@ -207,4 +219,194 @@ public final class WebRtcAudioUtils {
|
||||
+ "Model: " + Build.MODEL + ", "
|
||||
+ "Product: " + Build.PRODUCT);
|
||||
}
|
||||
|
||||
// Logs information about the current audio state. The idea is to call this
|
||||
// method when errors are detected to log under what conditions the error
|
||||
// occurred. Hopefully it will provide clues to what might be the root cause.
|
||||
static void logAudioState(String tag) {
|
||||
logDeviceInfo(tag);
|
||||
final Context context = ContextUtils.getApplicationContext();
|
||||
final AudioManager audioManager =
|
||||
(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
logAudioStateBasic(tag, audioManager);
|
||||
logAudioStateVolume(tag, audioManager);
|
||||
logAudioDeviceInfo(tag, audioManager);
|
||||
}
|
||||
|
||||
// Reports basic audio statistics.
|
||||
private static void logAudioStateBasic(String tag, AudioManager audioManager) {
|
||||
Logging.d(tag, "Audio State: "
|
||||
+ "audio mode: " + modeToString(audioManager.getMode()) + ", "
|
||||
+ "has mic: " + hasMicrophone() + ", "
|
||||
+ "mic muted: " + audioManager.isMicrophoneMute() + ", "
|
||||
+ "music active: " + audioManager.isMusicActive() + ", "
|
||||
+ "speakerphone: " + audioManager.isSpeakerphoneOn() + ", "
|
||||
+ "BT SCO: " + audioManager.isBluetoothScoOn());
|
||||
}
|
||||
|
||||
// Adds volume information for all possible stream types.
|
||||
private static void logAudioStateVolume(String tag, AudioManager audioManager) {
|
||||
final int[] streams = {
|
||||
AudioManager.STREAM_VOICE_CALL,
|
||||
AudioManager.STREAM_MUSIC,
|
||||
AudioManager.STREAM_RING,
|
||||
AudioManager.STREAM_ALARM,
|
||||
AudioManager.STREAM_NOTIFICATION,
|
||||
AudioManager.STREAM_SYSTEM
|
||||
};
|
||||
Logging.d(tag, "Audio State: ");
|
||||
boolean fixedVolume = false;
|
||||
if (WebRtcAudioUtils.runningOnLollipopOrHigher()) {
|
||||
fixedVolume = audioManager.isVolumeFixed();
|
||||
// Some devices may not have volume controls and might use a fixed volume.
|
||||
Logging.d(tag, " fixed volume=" + fixedVolume);
|
||||
}
|
||||
if (!fixedVolume) {
|
||||
for (int stream : streams) {
|
||||
StringBuilder info = new StringBuilder();
|
||||
info.append(" " + streamTypeToString(stream) + ": ");
|
||||
info.append("volume=").append(audioManager.getStreamVolume(stream));
|
||||
info.append(", max=").append(audioManager.getStreamMaxVolume(stream));
|
||||
logIsStreamMute(tag, audioManager, stream, info);
|
||||
Logging.d(tag, info.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
private static void logIsStreamMute(
|
||||
String tag, AudioManager audioManager, int stream, StringBuilder info) {
|
||||
if (WebRtcAudioUtils.runningOnMarshmallowOrHigher()) {
|
||||
info.append(", muted=").append(audioManager.isStreamMute(stream));
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
private static void logAudioDeviceInfo(String tag, AudioManager audioManager) {
|
||||
if (!WebRtcAudioUtils.runningOnMarshmallowOrHigher()) {
|
||||
return;
|
||||
}
|
||||
final AudioDeviceInfo[] devices =
|
||||
audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
|
||||
if (devices.length == 0) {
|
||||
return;
|
||||
}
|
||||
Logging.d(tag, "Audio Devices: ");
|
||||
for (AudioDeviceInfo device : devices) {
|
||||
StringBuilder info = new StringBuilder();
|
||||
info.append(" ").append(deviceTypeToString(device.getType()));
|
||||
info.append(device.isSource() ? "(in): " : "(out): ");
|
||||
// An empty array indicates that the device supports arbitrary channel counts.
|
||||
if (device.getChannelCounts().length > 0) {
|
||||
info.append("channels=").append(Arrays.toString(device.getChannelCounts()));
|
||||
info.append(", ");
|
||||
}
|
||||
if (device.getEncodings().length > 0) {
|
||||
// Examples: ENCODING_PCM_16BIT = 2, ENCODING_PCM_FLOAT = 4.
|
||||
info.append("encodings=").append(Arrays.toString(device.getEncodings()));
|
||||
info.append(", ");
|
||||
}
|
||||
if (device.getSampleRates().length > 0) {
|
||||
info.append("sample rates=").append(Arrays.toString(device.getSampleRates()));
|
||||
info.append(", ");
|
||||
}
|
||||
info.append("id=").append(device.getId());
|
||||
Logging.d(tag, info.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Converts media.AudioManager modes into local string representation.
|
||||
static String modeToString(int mode) {
|
||||
switch (mode) {
|
||||
case MODE_IN_CALL:
|
||||
return "MODE_IN_CALL";
|
||||
case MODE_IN_COMMUNICATION:
|
||||
return "MODE_IN_COMMUNICATION";
|
||||
case MODE_NORMAL:
|
||||
return "MODE_NORMAL";
|
||||
case MODE_RINGTONE:
|
||||
return "MODE_RINGTONE";
|
||||
default:
|
||||
return "MODE_INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
private static String streamTypeToString(int stream) {
|
||||
switch(stream) {
|
||||
case AudioManager.STREAM_VOICE_CALL:
|
||||
return "STREAM_VOICE_CALL";
|
||||
case AudioManager.STREAM_MUSIC:
|
||||
return "STREAM_MUSIC";
|
||||
case AudioManager.STREAM_RING:
|
||||
return "STREAM_RING";
|
||||
case AudioManager.STREAM_ALARM:
|
||||
return "STREAM_ALARM";
|
||||
case AudioManager.STREAM_NOTIFICATION:
|
||||
return "STREAM_NOTIFICATION";
|
||||
case AudioManager.STREAM_SYSTEM:
|
||||
return "STREAM_SYSTEM";
|
||||
default:
|
||||
return "STREAM_INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
// Converts AudioDeviceInfo types to local string representation.
|
||||
private static String deviceTypeToString(int type) {
|
||||
switch (type) {
|
||||
case AudioDeviceInfo.TYPE_UNKNOWN:
|
||||
return "TYPE_UNKNOWN";
|
||||
case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
|
||||
return "TYPE_BUILTIN_EARPIECE";
|
||||
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
|
||||
return "TYPE_BUILTIN_SPEAKER";
|
||||
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
|
||||
return "TYPE_WIRED_HEADSET";
|
||||
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
|
||||
return "TYPE_WIRED_HEADPHONES";
|
||||
case AudioDeviceInfo.TYPE_LINE_ANALOG:
|
||||
return "TYPE_LINE_ANALOG";
|
||||
case AudioDeviceInfo.TYPE_LINE_DIGITAL:
|
||||
return "TYPE_LINE_DIGITAL";
|
||||
case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
|
||||
return "TYPE_BLUETOOTH_SCO";
|
||||
case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
|
||||
return "TYPE_BLUETOOTH_A2DP";
|
||||
case AudioDeviceInfo.TYPE_HDMI:
|
||||
return "TYPE_HDMI";
|
||||
case AudioDeviceInfo.TYPE_HDMI_ARC:
|
||||
return "TYPE_HDMI_ARC";
|
||||
case AudioDeviceInfo.TYPE_USB_DEVICE:
|
||||
return "TYPE_USB_DEVICE";
|
||||
case AudioDeviceInfo.TYPE_USB_ACCESSORY:
|
||||
return "TYPE_USB_ACCESSORY";
|
||||
case AudioDeviceInfo.TYPE_DOCK:
|
||||
return "TYPE_DOCK";
|
||||
case AudioDeviceInfo.TYPE_FM:
|
||||
return "TYPE_FM";
|
||||
case AudioDeviceInfo.TYPE_BUILTIN_MIC:
|
||||
return "TYPE_BUILTIN_MIC";
|
||||
case AudioDeviceInfo.TYPE_FM_TUNER:
|
||||
return "TYPE_FM_TUNER";
|
||||
case AudioDeviceInfo.TYPE_TV_TUNER:
|
||||
return "TYPE_TV_TUNER";
|
||||
case AudioDeviceInfo.TYPE_TELEPHONY:
|
||||
return "TYPE_TELEPHONY";
|
||||
case AudioDeviceInfo.TYPE_AUX_LINE:
|
||||
return "TYPE_AUX_LINE";
|
||||
case AudioDeviceInfo.TYPE_IP:
|
||||
return "TYPE_IP";
|
||||
case AudioDeviceInfo.TYPE_BUS:
|
||||
return "TYPE_BUS";
|
||||
case AudioDeviceInfo.TYPE_USB_HEADSET:
|
||||
return "TYPE_USB_HEADSET";
|
||||
default:
|
||||
return "TYPE_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the device can record audio via a microphone.
|
||||
private static boolean hasMicrophone() {
|
||||
return ContextUtils.getApplicationContext().getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_MICROPHONE);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user