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..4f9cf6acd3fce3240384cbbaa9e4858fffb2d34e 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" |
| @@ -47,18 +48,12 @@ AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { |
| AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory) |
| : AudioManagerBase(audio_log_factory) { |
| SetMaxOutputStreamsAllowed(kMaxOutputStreams); |
| - |
| - j_audio_manager_.Reset( |
|
tommi (sloooow) - chröme
2014/02/04 14:30:12
yay!
henrika (OOO until Aug 14)
2014/02/05 09:45:57
thx
|
| - Java_AudioManagerAndroid_createAudioManagerAndroid( |
| - base::android::AttachCurrentThread(), |
| - base::android::GetApplicationContext(), |
| - reinterpret_cast<intptr_t>(this))); |
| - Init(); |
| } |
| AudioManagerAndroid::~AudioManagerAndroid() { |
| - Close(); |
| Shutdown(); |
| + // Verify that WillDestroyCurrentMessageLoop() has been called. |
| + DCHECK(j_audio_manager_.is_null()); |
| } |
| bool AudioManagerAndroid::HasAudioOutputDevices() { |
| @@ -71,26 +66,24 @@ 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); |
| + CreateAndInitOnAudioThread(); |
| + |
| + // 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(); |
| } |
| } |
| @@ -102,10 +95,15 @@ void AudioManagerAndroid::GetAudioOutputDeviceNames( |
| AudioParameters AudioManagerAndroid::GetInputStreamParameters( |
| const std::string& device_id) { |
| - JNIEnv* env = AttachCurrentThread(); |
| + CreateAndInitOnAudioThread(); |
| + |
| // Use mono as preferred number of input channels on Android to save |
| // resources. Using mono also avoids a driver issue seen on Samsung |
| // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details. |
| + // TODO(henrika): do we nee a requirement to call the rest of this method |
|
tommi (sloooow) - chröme
2014/02/04 14:30:12
typo
I assume this with regards to Dale's questio
henrika (OOO until Aug 14)
2014/02/05 09:45:57
Done.
|
| + // on the audio thread? |
| + JNIEnv* env = AttachCurrentThread(); |
| + CHECK(env); |
| ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO; |
| int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize( |
| env, GetNativeOutputSampleRate(), |
| @@ -146,6 +144,8 @@ AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( |
| AudioInputStream* AudioManagerAndroid::MakeAudioInputStream( |
| const AudioParameters& params, const std::string& device_id) { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + CreateAndInitOnAudioThread(); |
| bool had_no_streams = HadNoAudioStreams(); |
| AudioInputStream* stream = |
| AudioManagerBase::MakeAudioInputStream(params, device_id); |
| @@ -171,6 +171,8 @@ void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) { |
| } |
| void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + DCHECK(!j_audio_manager_.is_null()); |
| AudioManagerBase::ReleaseInputStream(stream); |
| // Restore the audio mode which was used before the first communication- |
| @@ -200,6 +202,8 @@ AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( |
| // needs it. |
| DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; |
| DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + CreateAndInitOnAudioThread(); |
| return new OpenSLESInputStream(this, params); |
| } |
| @@ -207,6 +211,9 @@ AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( |
| const AudioParameters& params, const std::string& device_id) { |
| DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); |
| DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!"; |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + CreateAndInitOnAudioThread(); |
| + |
| // Use the device ID to select the correct input device. |
| // Note that the input device is always associated with a certain output |
| // device, i.e., this selection does also switch the output device. |
| @@ -232,6 +239,10 @@ AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( |
| return new OpenSLESInputStream(this, params); |
| } |
| +void AudioManagerAndroid::WillDestroyCurrentMessageLoop() { |
| + CloseOnAudioThread(); |
| +} |
| + |
| int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate, |
| int channels) { |
| if (IsAudioLowLatencySupported()) { |
| @@ -282,6 +293,49 @@ bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) { |
| return RegisterNativesImpl(env); |
| } |
| +void AudioManagerAndroid::CreateAndInitOnAudioThread() { |
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner(GetTaskRunner()); |
| + if (task_runner->BelongsToCurrentThread()) { |
| + DoCreateAndInitOnAudioThread(NULL); |
| + } else { |
| + base::WaitableEvent event(false, false); |
| + task_runner->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &AudioManagerAndroid::DoCreateAndInitOnAudioThread, |
| + base::Unretained(this), |
| + &event)); |
| + event.Wait(); |
| + } |
| +} |
| + |
| +void AudioManagerAndroid::DoCreateAndInitOnAudioThread( |
| + base::WaitableEvent* event) { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + if (!j_audio_manager_.is_null()) |
| + return; |
| + |
| + JNIEnv* env = base::android::AttachCurrentThread(); |
| + CHECK(env); |
| + |
| + // Create the Android audio manager on the audio thread. |
| + j_audio_manager_.Reset( |
| + Java_AudioManagerAndroid_createAudioManagerAndroid( |
| + env, |
| + base::android::GetApplicationContext(), |
| + reinterpret_cast<intptr_t>(this))); |
| + |
| + // Prepare the list of audio devices and register receivers for device |
| + // notifications. |
| + Init(); |
| + |
| + // Ensure that we are notified when the audio thread dies. |
| + base::MessageLoop::current()->AddDestructionObserver(this); |
| + |
| + if (event) |
| + event->Signal(); |
| +} |
| + |
| void AudioManagerAndroid::Init() { |
| Java_AudioManagerAndroid_init( |
| base::android::AttachCurrentThread(), |
| @@ -303,26 +357,52 @@ 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()); |
| + |
| + // 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::CloseOnAudioThread() { |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| + if (j_audio_manager_.is_null()) |
| + return; |
| + Close(); |
| + j_audio_manager_.Reset(); |
| } |
| bool AudioManagerAndroid::SetAudioDevice(const std::string& device_id) { |
| - JNIEnv* env = AttachCurrentThread(); |
| + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); |
| // 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(); |
| + CHECK(env); |
| ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString( |
| env, |
| device_id == AudioManagerBase::kDefaultDeviceId ? |
| @@ -331,6 +411,21 @@ bool AudioManagerAndroid::SetAudioDevice(const std::string& device_id) { |
| env, j_audio_manager_.obj(), j_device_id.obj()); |
| } |
| +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() { |
| return Java_AudioManagerAndroid_getNativeOutputSampleRate( |
| base::android::AttachCurrentThread(), |