Fix for "Android audio playout doesn't support non-call media stream"
BUG=webrtc:4767
R=magjed@webrtc.org
Review URL: https://codereview.webrtc.org/1419693004 .
Cr-Original-Commit-Position: refs/heads/master@{#10435}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 6408174cdc4040528dd87ff7e5c76be91cdbafbe
diff --git a/modules/audio_device/android/audio_manager.cc b/modules/audio_device/android/audio_manager.cc
index 260e793..169a192 100644
--- a/modules/audio_device/android/audio_manager.cc
+++ b/modules/audio_device/android/audio_manager.cc
@@ -71,12 +71,13 @@
hardware_agc_(false),
hardware_ns_(false),
low_latency_playout_(false),
- delay_estimate_in_milliseconds_(0) {
+ delay_estimate_in_milliseconds_(0),
+ output_stream_type_(0) {
ALOGD("ctor%s", GetThreadInfo().c_str());
RTC_CHECK(j_environment_);
JNINativeMethod native_methods[] = {
{"nativeCacheAudioParameters",
- "(IIZZZZIIJ)V",
+ "(IIZZZZIIIJ)V",
reinterpret_cast<void*>(&webrtc::AudioManager::CacheAudioParameters)}};
j_native_registration_ = j_environment_->RegisterNatives(
"org/webrtc/voiceengine/WebRtcAudioManager",
@@ -179,12 +180,14 @@
jboolean low_latency_output,
jint output_buffer_size,
jint input_buffer_size,
+ jint output_stream_type,
jlong native_audio_manager) {
webrtc::AudioManager* this_object =
reinterpret_cast<webrtc::AudioManager*>(native_audio_manager);
this_object->OnCacheAudioParameters(
env, sample_rate, channels, hardware_aec, hardware_agc, hardware_ns,
- low_latency_output, output_buffer_size, input_buffer_size);
+ low_latency_output, output_buffer_size, input_buffer_size,
+ output_stream_type);
}
void AudioManager::OnCacheAudioParameters(JNIEnv* env,
@@ -195,7 +198,8 @@
jboolean hardware_ns,
jboolean low_latency_output,
jint output_buffer_size,
- jint input_buffer_size) {
+ jint input_buffer_size,
+ jint output_stream_type) {
ALOGD("OnCacheAudioParameters%s", GetThreadInfo().c_str());
ALOGD("hardware_aec: %d", hardware_aec);
ALOGD("hardware_agc: %d", hardware_agc);
@@ -205,11 +209,13 @@
ALOGD("channels: %d", channels);
ALOGD("output_buffer_size: %d", output_buffer_size);
ALOGD("input_buffer_size: %d", input_buffer_size);
+ ALOGD("output_stream_type: %d", output_stream_type);
RTC_DCHECK(thread_checker_.CalledOnValidThread());
hardware_aec_ = hardware_aec;
hardware_agc_ = hardware_agc;
hardware_ns_ = hardware_ns;
low_latency_playout_ = low_latency_output;
+ output_stream_type_ = output_stream_type;
// TODO(henrika): add support for stereo output.
playout_parameters_.reset(sample_rate, channels,
static_cast<size_t>(output_buffer_size));
diff --git a/modules/audio_device/android/audio_manager.h b/modules/audio_device/android/audio_manager.h
index 9cceaac..5f23147 100644
--- a/modules/audio_device/android/audio_manager.h
+++ b/modules/audio_device/android/audio_manager.h
@@ -93,6 +93,8 @@
// webrtc::kHighLatencyModeDelayEstimateInMilliseconds.
int GetDelayEstimateInMilliseconds() const;
+ int OutputStreamType() const { return output_stream_type_; }
+
private:
// Called from Java side so we can cache the native audio parameters.
// This method will be called by the WebRtcAudioManager constructor, i.e.
@@ -107,6 +109,7 @@
jboolean low_latency_output,
jint output_buffer_size,
jint input_buffer_size,
+ jint output_stream_type,
jlong native_audio_manager);
void OnCacheAudioParameters(JNIEnv* env,
jint sample_rate,
@@ -116,7 +119,8 @@
jboolean hardware_ns,
jboolean low_latency_output,
jint output_buffer_size,
- jint input_buffer_size);
+ jint input_buffer_size,
+ jint output_stream_type);
// Stores thread ID in the constructor.
// We can then use ThreadChecker::CalledOnValidThread() to ensure that
@@ -155,6 +159,13 @@
// device supports low-latency output or not.
int delay_estimate_in_milliseconds_;
+ // Contains the output stream type provided to this class at construction by
+ // the AudioManager in Java land. Possible values are:
+ // - AudioManager.STREAM_VOICE_CALL = 0
+ // - AudioManager.STREAM_RING = 2
+ // - AudioManager.STREAM_MUSIC = 3
+ int output_stream_type_;
+
// Contains native parameters (e.g. sample rate, channel configuration).
// Set at construction in OnCacheAudioParameters() which is called from
// Java on the same thread as this object is created on.
diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java
index 10fe8ca..cf2f03a 100644
--- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java
+++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java
@@ -71,6 +71,7 @@
private int channels;
private int outputBufferSize;
private int inputBufferSize;
+ private int outputStreamType;
WebRtcAudioManager(Context context, long nativeAudioManager) {
Logging.d(TAG, "ctor" + WebRtcAudioUtils.getThreadInfo());
@@ -84,7 +85,7 @@
storeAudioParameters();
nativeCacheAudioParameters(
sampleRate, channels, hardwareAEC, hardwareAGC, hardwareNS,
- lowLatencyOutput, outputBufferSize, inputBufferSize,
+ lowLatencyOutput, outputBufferSize, inputBufferSize, outputStreamType,
nativeAudioManager);
}
@@ -132,6 +133,8 @@
getMinOutputFrameSize(sampleRate, channels);
// TODO(henrika): add support for low-latency input.
inputBufferSize = getMinInputFrameSize(sampleRate, channels);
+ outputStreamType = WebRtcAudioUtils.getOutputStreamTypeFromAudioMode(
+ audioManager.getMode());
}
// Gets the current earpiece state.
@@ -267,5 +270,5 @@
private native void nativeCacheAudioParameters(
int sampleRate, int channels, boolean hardwareAEC, boolean hardwareAGC,
boolean hardwareNS, boolean lowLatencyOutput, int outputBufferSize,
- int inputBufferSize, long nativeAudioManager);
+ int inputBufferSize, int outputStreamType, long nativeAudioManager);
}
diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java
index ff77635..7b31e08 100644
--- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java
+++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java
@@ -192,6 +192,10 @@
Math.max(BUFFER_SIZE_FACTOR * minBufferSize, byteBuffer.capacity());
Logging.d(TAG, "bufferSizeInBytes: " + bufferSizeInBytes);
try {
+ // TODO(henrika): the only supported audio source for input is currently
+ // AudioSource.VOICE_COMMUNICATION. Is there any reason why we should
+ // support other types, e.g. DEFAULT or MIC? Only reason I can think of
+ // is if the device does not support VOICE_COMMUNICATION.
audioRecord = new AudioRecord(AudioSource.VOICE_COMMUNICATION,
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java
index 0602e44..ec0e109 100644
--- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java
+++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java
@@ -39,6 +39,7 @@
private final Context context;
private final long nativeAudioTrack;
private final AudioManager audioManager;
+ private final int streamType;
private ByteBuffer byteBuffer;
@@ -141,6 +142,9 @@
this.nativeAudioTrack = nativeAudioTrack;
audioManager = (AudioManager) context.getSystemService(
Context.AUDIO_SERVICE);
+ this.streamType =
+ WebRtcAudioUtils.getOutputStreamTypeFromAudioMode(
+ audioManager.getMode());
if (DEBUG) {
WebRtcAudioUtils.logDeviceInfo(TAG);
}
@@ -177,7 +181,7 @@
// Create an AudioTrack object and initialize its associated audio buffer.
// The size of this buffer determines how long an AudioTrack can play
// before running out of data.
- audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
+ audioTrack = new AudioTrack(streamType,
sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
@@ -189,7 +193,7 @@
}
assertTrue(audioTrack.getState() == AudioTrack.STATE_INITIALIZED);
assertTrue(audioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED);
- assertTrue(audioTrack.getStreamType() == AudioManager.STREAM_VOICE_CALL);
+ assertTrue(audioTrack.getStreamType() == streamType);
}
private boolean startPlayout() {
@@ -213,14 +217,14 @@
return true;
}
- /** Get max possible volume index for a phone call audio stream. */
+ /** Get max possible volume index given type of audio stream. */
private int getStreamMaxVolume() {
Logging.d(TAG, "getStreamMaxVolume");
assertTrue(audioManager != null);
- return audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
+ return audioManager.getStreamMaxVolume(streamType);
}
- /** Set current volume level for a phone call audio stream. */
+ /** Set current volume level given type of audio stream. */
private boolean setStreamVolume(int volume) {
Logging.d(TAG, "setStreamVolume(" + volume + ")");
assertTrue(audioManager != null);
@@ -230,15 +234,15 @@
return false;
}
}
- audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, volume, 0);
+ audioManager.setStreamVolume(streamType, volume, 0);
return true;
}
- /** Get current volume level for a phone call audio stream. */
+ /** Get current volume level given type of audio stream. */
private int getStreamVolume() {
Logging.d(TAG, "getStreamVolume");
assertTrue(audioManager != null);
- return audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+ return audioManager.getStreamVolume(streamType);
}
/** Helper method which throws an exception when an assertion has failed. */
diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java
index 9d7a600..f08e11d 100644
--- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java
+++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java
@@ -193,5 +193,37 @@
permission,
Process.myPid(),
Process.myUid()) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ // Convert the provided audio |mode| into most suitable audio output stream
+ // type. The stream type is used for creating audio streams and for volume
+ // changes. It is essential that the mode and type are in-line to ensure
+ // correct behavior. If for example a STREAM_MUSIC type of stream is created
+ // in a MODE_IN_COMMUNICATION mode, audio will be played out and the volume
+ // icon will look OK but the actual volume will not be changed when the user
+ // changes the volume slider.
+ // TODO(henrika): there is currently no mapping to STREAM_ALARM, STREAM_DTMF,
+ // or STREAM_NOTIFICATION types since I am unable to see a reason for using
+ // them. There are only four different modes.
+ public static int getOutputStreamTypeFromAudioMode(int mode) {
+ Logging.d(TAG, "getOutputStreamTypeFromAudioMode(mode=" + mode + ")");
+ switch (mode) {
+ case AudioManager.MODE_NORMAL:
+ // The audio stream for music playback.
+ Logging.d(TAG, "AudioManager.STREAM_MUSIC");
+ return AudioManager.STREAM_MUSIC;
+ case AudioManager.MODE_RINGTONE:
+ // Audio stream for the phone ring.
+ Logging.d(TAG, "AudioManager.STREAM_RING");
+ return AudioManager.STREAM_RING;
+ case AudioManager.MODE_IN_CALL:
+ case AudioManager.MODE_IN_COMMUNICATION:
+ // Audio stream for phone calls.
+ Logging.d(TAG, "AudioManager.STREAM_VOICE_CALL");
+ return AudioManager.STREAM_VOICE_CALL;
+ default:
+ Logging.d(TAG, "AudioManager.USE_DEFAULT_STREAM_TYPE");
+ return AudioManager.USE_DEFAULT_STREAM_TYPE;
}
+ }
}
diff --git a/modules/audio_device/android/opensles_player.cc b/modules/audio_device/android/opensles_player.cc
index b9ccfd5..40967c5 100644
--- a/modules/audio_device/android/opensles_player.cc
+++ b/modules/audio_device/android/opensles_player.cc
@@ -38,6 +38,7 @@
OpenSLESPlayer::OpenSLESPlayer(AudioManager* audio_manager)
: audio_parameters_(audio_manager->GetPlayoutAudioParameters()),
+ stream_type_(audio_manager->OutputStreamType()),
audio_device_buffer_(NULL),
initialized_(false),
playing_(false),
@@ -48,6 +49,9 @@
simple_buffer_queue_(nullptr),
volume_(nullptr) {
ALOGD("ctor%s", GetThreadInfo().c_str());
+ RTC_DCHECK(stream_type_ == SL_ANDROID_STREAM_VOICE ||
+ stream_type_ == SL_ANDROID_STREAM_RING ||
+ stream_type_ == SL_ANDROID_STREAM_MEDIA) << stream_type_;
// Use native audio output parameters provided by the audio manager and
// define the PCM format structure.
pcm_format_ = CreatePCMConfiguration(audio_parameters_.channels(),
@@ -347,7 +351,7 @@
false);
// Set audio player configuration to SL_ANDROID_STREAM_VOICE which
// corresponds to android.media.AudioManager.STREAM_VOICE_CALL.
- SLint32 stream_type = SL_ANDROID_STREAM_VOICE;
+ SLint32 stream_type = stream_type_;
RETURN_ON_ERROR(
(*player_config)
->SetConfiguration(player_config, SL_ANDROID_KEY_STREAM_TYPE,
diff --git a/modules/audio_device/android/opensles_player.h b/modules/audio_device/android/opensles_player.h
index d96388b..96b1d49 100644
--- a/modules/audio_device/android/opensles_player.h
+++ b/modules/audio_device/android/opensles_player.h
@@ -130,6 +130,20 @@
// AudioManager.
const AudioParameters audio_parameters_;
+ // Contains the stream type provided to this class at construction by the
+ // AudioManager. Possible input values are:
+ // - AudioManager.STREAM_VOICE_CALL = 0
+ // - AudioManager.STREAM_RING = 2
+ // - AudioManager.STREAM_MUSIC = 3
+ // These value are mapped to the corresponding audio playback stream type
+ // values in the "OpenSL ES domain":
+ // - SL_ANDROID_STREAM_VOICE <=> STREAM_VOICE_CALL (0)
+ // - SL_ANDROID_STREAM_RING <=> STREAM_RING (2)
+ // - SL_ANDROID_STREAM_MEDIA <=> STREAM_MUSIC (3)
+ // when creating the audio player. See SLES/OpenSLES_AndroidConfiguration.h
+ // for details.
+ const int stream_type_;
+
// Raw pointer handle provided to us in AttachAudioBuffer(). Owned by the
// AudioDeviceModuleImpl class and called by AudioDeviceModuleImpl::Create().
AudioDeviceBuffer* audio_device_buffer_;