Chromium Code Reviews| Index: media/audio/android/audio_manager_android.cc |
| diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc |
| index 33e9e085688de87c37eebfbdbfdeddfb9949f2db..af61379a42d9610ebabd3392993a0615572f78e7 100644 |
| --- a/media/audio/android/audio_manager_android.cc |
| +++ b/media/audio/android/audio_manager_android.cc |
| @@ -9,6 +9,7 @@ |
| #include "base/android/jni_string.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/logging.h" |
| +#include "base/message_loop/message_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "jni/AudioManagerAndroid_jni.h" |
| #include "media/audio/android/audio_record_input.h" |
| @@ -45,9 +46,9 @@ AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { |
| } |
| AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory) |
| - : AudioManagerBase(audio_log_factory) { |
| + : AudioManagerBase(audio_log_factory), |
| + initialized_on_audio_thread_(false) { |
| SetMaxOutputStreamsAllowed(kMaxOutputStreams); |
| - |
| j_audio_manager_.Reset( |
| Java_AudioManagerAndroid_createAudioManagerAndroid( |
| base::android::AttachCurrentThread(), |
| @@ -59,6 +60,8 @@ AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory) |
| AudioManagerAndroid::~AudioManagerAndroid() { |
| Close(); |
| Shutdown(); |
| + // Verify that WillDestroyCurrentMessageLoop() has been called. |
| + DCHECK(!initialized_on_audio_thread_); |
| } |
| bool AudioManagerAndroid::HasAudioOutputDevices() { |
| @@ -71,26 +74,21 @@ bool AudioManagerAndroid::HasAudioInputDevices() { |
| void AudioManagerAndroid::GetAudioInputDeviceNames( |
| AudioDeviceNames* device_names) { |
| - // Always add default device parameters as first element. |
| - DCHECK(device_names->empty()); |
| - AddDefaultDevice(device_names); |
| - |
| - JNIEnv* env = AttachCurrentThread(); |
| - ScopedJavaLocalRef<jobjectArray> j_device_array = |
| - Java_AudioManagerAndroid_getAudioInputDeviceNames( |
| - env, j_audio_manager_.obj()); |
| - jsize len = env->GetArrayLength(j_device_array.obj()); |
| - AudioDeviceName device; |
| - for (jsize i = 0; i < len; ++i) { |
| - ScopedJavaLocalRef<jobject> j_device( |
| - env, env->GetObjectArrayElement(j_device_array.obj(), i)); |
| - ScopedJavaLocalRef<jstring> j_device_name = |
| - Java_AudioDeviceName_name(env, j_device.obj()); |
| - ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name); |
| - ScopedJavaLocalRef<jstring> j_device_id = |
| - Java_AudioDeviceName_id(env, j_device.obj()); |
| - ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id); |
| - device_names->push_back(device); |
| + // Get list of available audio devices and use the audio thread to reduce |
| + // the number of calling threads to the Java layer. Also, ensure that the |
| + // calling thread sees this function call as synchronous. |
| + if (GetTaskRunner()->BelongsToCurrentThread()) { |
| + GetAudioInputDeviceNamesOnAudioThread(NULL, device_names); |
| + } else { |
| + base::WaitableEvent event(false, false); |
| + GetTaskRunner()->PostTask( |
|
tommi (sloooow) - chröme
2014/01/30 12:51:11
nit: You could avoid calling GetTaskRunner more th
henrika (OOO until Aug 14)
2014/01/30 14:48:24
Done.
|
| + FROM_HERE, |
| + base::Bind( |
| + &AudioManagerAndroid::GetAudioInputDeviceNamesOnAudioThread, |
| + base::Unretained(this), |
| + &event, |
| + device_names)); |
| + event.Wait(); |
| } |
| } |
| @@ -232,6 +230,13 @@ AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( |
| return new OpenSLESInputStream(this, params); |
| } |
| +void AudioManagerAndroid::WillDestroyCurrentMessageLoop() { |
| + // This call matches previous calls to InitializeOnAudioThread() and it |
| + // ensures that we unregister receivers for events related to changes in |
| + // availability of audio devices. |
| + CloseOnAudioThread(); |
| +} |
| + |
| int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate, |
| int channels) { |
| if (IsAudioLowLatencySupported()) { |
| @@ -303,32 +308,123 @@ void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) { |
| muted)); |
| } |
| -void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) { |
| - base::AutoLock lock(streams_lock_); |
| - for (OutputStreams::iterator it = streams_.begin(); |
| - it != streams_.end(); ++it) { |
| - (*it)->SetMute(muted); |
| +void AudioManagerAndroid::GetAudioInputDeviceNamesOnAudioThread( |
| + base::WaitableEvent* event, AudioDeviceNames* device_names) { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + // Set up the initial list of available audio devices and register for |
| + // broadcasted notifications about any changes of availability. |
| + // This method is only executed when called for the first time and it can |
| + // also be called from SetAudioDeviceOnAudioThread(). |
| + InitializeOnAudioThread(); |
| + |
| + // Always add default device parameters as first element. |
| + DCHECK(device_names->empty()); |
| + AddDefaultDevice(device_names); |
| + |
| + JNIEnv* env = AttachCurrentThread(); |
| + ScopedJavaLocalRef<jobjectArray> j_device_array = |
| + Java_AudioManagerAndroid_getAudioInputDeviceNames( |
| + env, j_audio_manager_.obj()); |
| + jsize len = env->GetArrayLength(j_device_array.obj()); |
| + AudioDeviceName device; |
| + for (jsize i = 0; i < len; ++i) { |
| + ScopedJavaLocalRef<jobject> j_device( |
| + env, env->GetObjectArrayElement(j_device_array.obj(), i)); |
| + ScopedJavaLocalRef<jstring> j_device_name = |
| + Java_AudioDeviceName_name(env, j_device.obj()); |
| + ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name); |
| + ScopedJavaLocalRef<jstring> j_device_id = |
| + Java_AudioDeviceName_id(env, j_device.obj()); |
| + ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id); |
| + device_names->push_back(device); |
| } |
| + |
| + if (event) |
| + event->Signal(); |
| } |
| -void AudioManagerAndroid::SetCommunicationAudioModeOn(bool on) { |
| - Java_AudioManagerAndroid_setCommunicationAudioModeOn( |
| - base::android::AttachCurrentThread(), |
| - j_audio_manager_.obj(), on); |
| +void AudioManagerAndroid::InitializeOnAudioThread() { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + if (!initialized_on_audio_thread_) { |
| + base::MessageLoop::current()->AddDestructionObserver(this); |
| + Java_AudioManagerAndroid_initAudioDeviceList( |
| + base::android::AttachCurrentThread(), |
| + j_audio_manager_.obj()); |
| + initialized_on_audio_thread_ = true; |
| + } |
| +} |
| + |
| +void AudioManagerAndroid::CloseOnAudioThread() { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + if (!initialized_on_audio_thread_) |
| + return; |
| + Java_AudioManagerAndroid_closeAudioDeviceList( |
| + base::android::AttachCurrentThread(), |
| + j_audio_manager_.obj()); |
| + initialized_on_audio_thread_ = false; |
| } |
| bool AudioManagerAndroid::SetAudioDevice(const std::string& device_id) { |
| - JNIEnv* env = AttachCurrentThread(); |
| + // Select audio device and use the audio thread to reduce the number of |
| + // calling threads to the Java layer. Also, ensure that the calling thread |
| + // sees this function call as synchronous. |
| + bool result = false; |
| + if (GetTaskRunner()->BelongsToCurrentThread()) { |
| + SetAudioDeviceOnAudioThread(NULL, device_id, &result); |
| + } else { |
| + base::WaitableEvent event(false, false); |
|
tommi (sloooow) - chröme
2014/01/30 12:51:11
When is SetAudioDevice called outside of the audio
henrika (OOO until Aug 14)
2014/01/30 14:48:24
I will have to fix the unit test to do so. Discuss
|
| + GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &AudioManagerAndroid::SetAudioDeviceOnAudioThread, |
| + base::Unretained(this), |
| + &event, |
| + device_id, |
| + &result)); |
| + event.Wait(); |
| + } |
| + return result; |
| +} |
| + |
| +void AudioManagerAndroid::SetAudioDeviceOnAudioThread( |
| + base::WaitableEvent* event, const std::string& device_id, bool* result) { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + // Set up the initial list of available audio devices and register for |
| + // broadcasted notifications about any changes of availability. |
| + // This method is only executed when called for the first time and it can |
| + // also be called from GetAudioInputDeviceNamesOnAudioThread(). |
| + InitializeOnAudioThread(); |
| // Send the unique device ID to the Java audio manager and make the |
| // device switch. Provide an empty string to the Java audio manager |
| // if the default device is selected. |
| + JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString( |
| env, |
| device_id == AudioManagerBase::kDefaultDeviceId ? |
| std::string() : device_id); |
| - return Java_AudioManagerAndroid_setDevice( |
| + *result = Java_AudioManagerAndroid_setDevice( |
| env, j_audio_manager_.obj(), j_device_id.obj()); |
| + |
| + if (event) |
| + event->Signal(); |
| +} |
| + |
| +void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + base::AutoLock lock(streams_lock_); |
| + for (OutputStreams::iterator it = streams_.begin(); |
| + it != streams_.end(); ++it) { |
| + (*it)->SetMute(muted); |
| + } |
| +} |
| + |
| +void AudioManagerAndroid::SetCommunicationAudioModeOn(bool on) { |
| + Java_AudioManagerAndroid_setCommunicationAudioModeOn( |
| + base::android::AttachCurrentThread(), |
| + j_audio_manager_.obj(), on); |
| } |
| int AudioManagerAndroid::GetNativeOutputSampleRate() { |