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..1bfcbac2eb816c2ed08216a73b3beec71c528363 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,22 @@ 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. |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner(GetTaskRunner()); |
+ if (task_runner->BelongsToCurrentThread()) { |
+ GetAudioInputDeviceNamesOnAudioThread(NULL, device_names); |
+ } else { |
+ base::WaitableEvent event(false, false); |
+ task_runner->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &AudioManagerAndroid::GetAudioInputDeviceNamesOnAudioThread, |
+ base::Unretained(this), |
+ &event, |
+ device_names)); |
+ event.Wait(); |
} |
} |
@@ -146,6 +145,7 @@ AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( |
AudioInputStream* AudioManagerAndroid::MakeAudioInputStream( |
const AudioParameters& params, const std::string& device_id) { |
+ DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
bool had_no_streams = HadNoAudioStreams(); |
AudioInputStream* stream = |
AudioManagerBase::MakeAudioInputStream(params, device_id); |
@@ -206,6 +206,7 @@ AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( |
AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( |
const AudioParameters& params, const std::string& device_id) { |
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); |
+ // DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
tommi (sloooow) - chröme
2014/01/30 15:26:04
will you uncomment this before checkin?
henrika (OOO until Aug 14)
2014/01/30 17:20:23
Done.
|
DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!"; |
// Use the device ID to select the correct input device. |
// Note that the input device is always associated with a certain output |
@@ -232,6 +233,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 +311,124 @@ 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; |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner(GetTaskRunner()); |
+ if (task_runner->BelongsToCurrentThread()) { |
+ SetAudioDeviceOnAudioThread(NULL, device_id, &result); |
+ } else { |
+ base::WaitableEvent event(false, false); |
+ task_runner->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() { |