audio_manager_android.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/audio/android/audio_manager_android.h"
6
7#include "base/android/build_info.h"
8#include "base/android/jni_array.h"
9#include "base/android/jni_string.h"
10#include "base/android/scoped_java_ref.h"
11#include "base/logging.h"
12#include "base/message_loop/message_loop.h"
13#include "base/strings/string_number_conversions.h"
14#include "jni/AudioManagerAndroid_jni.h"
15#include "media/audio/android/audio_record_input.h"
16#include "media/audio/android/opensles_input.h"
17#include "media/audio/android/opensles_output.h"
18#include "media/audio/audio_manager.h"
19#include "media/audio/audio_parameters.h"
20#include "media/audio/fake_audio_input_stream.h"
21#include "media/base/channel_layout.h"
22
23using base::android::AppendJavaStringArrayToStringVector;
24using base::android::AttachCurrentThread;
25using base::android::ConvertJavaStringToUTF8;
26using base::android::ConvertUTF8ToJavaString;
27using base::android::ScopedJavaLocalRef;
28
29namespace media {
30
31static void AddDefaultDevice(AudioDeviceNames* device_names) {
32  DCHECK(device_names->empty());
33  device_names->push_front(
34      AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
35                      AudioManagerBase::kDefaultDeviceId));
36}
37
38// Maximum number of output streams that can be open simultaneously.
39static const int kMaxOutputStreams = 10;
40
41static const int kDefaultInputBufferSize = 1024;
42static const int kDefaultOutputBufferSize = 2048;
43
44AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
45  return new AudioManagerAndroid(audio_log_factory);
46}
47
48AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory)
49    : AudioManagerBase(audio_log_factory),
50      communication_mode_is_on_(false) {
51  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
52
53  // WARNING: This is executed on the UI loop, do not add any code here which
54  // loads libraries or attempts to call out into the OS.  Instead add such code
55  // to the InitializeOnAudioThread() method below.
56
57  // Task must be posted last to avoid races from handing out "this" to the
58  // audio thread.
59  GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
60      &AudioManagerAndroid::InitializeOnAudioThread,
61      base::Unretained(this)));
62}
63
64AudioManagerAndroid::~AudioManagerAndroid() {
65  // It's safe to post a task here since Shutdown() will wait for all tasks to
66  // complete before returning.
67  GetTaskRunner()->PostTask(FROM_HERE, base::Bind(
68      &AudioManagerAndroid::ShutdownOnAudioThread, base::Unretained(this)));
69  Shutdown();
70}
71
72bool AudioManagerAndroid::HasAudioOutputDevices() {
73  return true;
74}
75
76bool AudioManagerAndroid::HasAudioInputDevices() {
77  return true;
78}
79
80void AudioManagerAndroid::GetAudioInputDeviceNames(
81    AudioDeviceNames* device_names) {
82  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
83
84  // Always add default device parameters as first element.
85  DCHECK(device_names->empty());
86  AddDefaultDevice(device_names);
87
88  // Get list of available audio devices.
89  JNIEnv* env = AttachCurrentThread();
90  ScopedJavaLocalRef<jobjectArray> j_device_array =
91      Java_AudioManagerAndroid_getAudioInputDeviceNames(
92          env, j_audio_manager_.obj());
93  jsize len = env->GetArrayLength(j_device_array.obj());
94  AudioDeviceName device;
95  for (jsize i = 0; i < len; ++i) {
96    ScopedJavaLocalRef<jobject> j_device(
97        env, env->GetObjectArrayElement(j_device_array.obj(), i));
98    ScopedJavaLocalRef<jstring> j_device_name =
99        Java_AudioDeviceName_name(env, j_device.obj());
100    ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name);
101    ScopedJavaLocalRef<jstring> j_device_id =
102        Java_AudioDeviceName_id(env, j_device.obj());
103    ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id);
104    device_names->push_back(device);
105  }
106}
107
108void AudioManagerAndroid::GetAudioOutputDeviceNames(
109    AudioDeviceNames* device_names) {
110  // TODO(henrika): enumerate using GetAudioInputDeviceNames().
111  AddDefaultDevice(device_names);
112}
113
114AudioParameters AudioManagerAndroid::GetInputStreamParameters(
115    const std::string& device_id) {
116  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
117
118  // Use mono as preferred number of input channels on Android to save
119  // resources. Using mono also avoids a driver issue seen on Samsung
120  // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details.
121  JNIEnv* env = AttachCurrentThread();
122  ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO;
123  int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize(
124      env, GetNativeOutputSampleRate(),
125      ChannelLayoutToChannelCount(channel_layout));
126  int effects = AudioParameters::NO_EFFECTS;
127  effects |= Java_AudioManagerAndroid_shouldUseAcousticEchoCanceler(env) ?
128      AudioParameters::ECHO_CANCELLER : AudioParameters::NO_EFFECTS;
129  AudioParameters params(
130      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 0,
131      GetNativeOutputSampleRate(), 16,
132      buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size, effects);
133  return params;
134}
135
136AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
137    const AudioParameters& params,
138    const std::string& device_id) {
139  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
140  AudioOutputStream* stream =
141      AudioManagerBase::MakeAudioOutputStream(params, std::string());
142  streams_.insert(static_cast<OpenSLESOutputStream*>(stream));
143  return stream;
144}
145
146AudioInputStream* AudioManagerAndroid::MakeAudioInputStream(
147    const AudioParameters& params, const std::string& device_id) {
148  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
149  bool has_no_input_streams = HasNoAudioInputStreams();
150  AudioInputStream* stream =
151      AudioManagerBase::MakeAudioInputStream(params, device_id);
152
153  // The audio manager for Android creates streams intended for real-time
154  // VoIP sessions and therefore sets the audio mode to MODE_IN_COMMUNICATION.
155  // If a Bluetooth headset is used, the audio stream will use the SCO
156  // channel and therefore have a limited bandwidth (8kHz).
157  if (stream && has_no_input_streams) {
158    communication_mode_is_on_ = true;
159    SetCommunicationAudioModeOn(true);
160  }
161  return stream;
162}
163
164void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) {
165  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
166  AudioManagerBase::ReleaseOutputStream(stream);
167  streams_.erase(static_cast<OpenSLESOutputStream*>(stream));
168}
169
170void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) {
171  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
172  DCHECK(!j_audio_manager_.is_null());
173  AudioManagerBase::ReleaseInputStream(stream);
174
175  // Restore the audio mode which was used before the first communication-
176  // mode stream was created.
177  if (HasNoAudioInputStreams()) {
178    communication_mode_is_on_ = false;
179    SetCommunicationAudioModeOn(false);
180  }
181}
182
183AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream(
184    const AudioParameters& params) {
185  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
186  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
187  return new OpenSLESOutputStream(this, params, SL_ANDROID_STREAM_MEDIA);
188}
189
190AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
191    const AudioParameters& params,
192    const std::string& device_id) {
193  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
194  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
195
196  // Set stream type which matches the current system-wide audio mode used by
197  // the Android audio manager.
198  const SLint32 stream_type = communication_mode_is_on_ ?
199      SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
200  return new OpenSLESOutputStream(this, params, stream_type);
201}
202
203AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
204    const AudioParameters& params, const std::string& device_id) {
205  // TODO(henrika): add support for device selection if/when any client
206  // needs it.
207  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
208  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
209  return new OpenSLESInputStream(this, params);
210}
211
212AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream(
213    const AudioParameters& params, const std::string& device_id) {
214  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
215  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
216  DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!";
217
218  // Use the device ID to select the correct input device.
219  // Note that the input device is always associated with a certain output
220  // device, i.e., this selection does also switch the output device.
221  // All input and output streams will be affected by the device selection.
222  if (!SetAudioDevice(device_id)) {
223    LOG(ERROR) << "Unable to select audio device!";
224    return NULL;
225  }
226
227  if (params.effects() != AudioParameters::NO_EFFECTS) {
228    // Platform effects can only be enabled through the AudioRecord path.
229    // An effect should only have been requested here if recommended by
230    // AudioManagerAndroid.shouldUse<Effect>.
231    //
232    // Creating this class requires Jelly Bean, which is already guaranteed by
233    // shouldUse<Effect>. Only DCHECK on that condition to allow tests to use
234    // the effect settings as a way to select the input path.
235    DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(), 16);
236    DVLOG(1) << "Creating AudioRecordInputStream";
237    return new AudioRecordInputStream(this, params);
238  }
239  DVLOG(1) << "Creating OpenSLESInputStream";
240  return new OpenSLESInputStream(this, params);
241}
242
243// static
244bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) {
245  return RegisterNativesImpl(env);
246}
247
248void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) {
249  GetTaskRunner()->PostTask(
250      FROM_HERE,
251      base::Bind(
252          &AudioManagerAndroid::DoSetMuteOnAudioThread,
253          base::Unretained(this),
254          muted));
255}
256
257AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters(
258    const std::string& output_device_id,
259    const AudioParameters& input_params) {
260  // TODO(tommi): Support |output_device_id|.
261  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
262  DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
263  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
264  int sample_rate = GetNativeOutputSampleRate();
265  int buffer_size = GetOptimalOutputFrameSize(sample_rate, 2);
266  int bits_per_sample = 16;
267  int input_channels = 0;
268  if (input_params.IsValid()) {
269    // Use the client's input parameters if they are valid.
270    sample_rate = input_params.sample_rate();
271    bits_per_sample = input_params.bits_per_sample();
272    channel_layout = input_params.channel_layout();
273    input_channels = input_params.input_channels();
274    buffer_size = GetOptimalOutputFrameSize(
275        sample_rate, ChannelLayoutToChannelCount(channel_layout));
276  }
277
278  int user_buffer_size = GetUserBufferSize();
279  if (user_buffer_size)
280    buffer_size = user_buffer_size;
281
282  return AudioParameters(
283      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
284      sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
285}
286
287bool AudioManagerAndroid::HasNoAudioInputStreams() {
288  return input_stream_count() == 0;
289}
290
291void AudioManagerAndroid::InitializeOnAudioThread() {
292  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
293
294  // Create the Android audio manager on the audio thread.
295  DVLOG(2) << "Creating Java part of the audio manager";
296  j_audio_manager_.Reset(
297      Java_AudioManagerAndroid_createAudioManagerAndroid(
298          base::android::AttachCurrentThread(),
299          base::android::GetApplicationContext(),
300          reinterpret_cast<intptr_t>(this)));
301
302  // Prepare the list of audio devices and register receivers for device
303  // notifications.
304  Java_AudioManagerAndroid_init(
305      base::android::AttachCurrentThread(),
306      j_audio_manager_.obj());
307}
308
309void AudioManagerAndroid::ShutdownOnAudioThread() {
310  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
311  DVLOG(2) << "Destroying Java part of the audio manager";
312  Java_AudioManagerAndroid_close(
313      base::android::AttachCurrentThread(),
314      j_audio_manager_.obj());
315  j_audio_manager_.Reset();
316}
317
318void AudioManagerAndroid::SetCommunicationAudioModeOn(bool on) {
319  Java_AudioManagerAndroid_setCommunicationAudioModeOn(
320      base::android::AttachCurrentThread(),
321      j_audio_manager_.obj(), on);
322}
323
324bool AudioManagerAndroid::SetAudioDevice(const std::string& device_id) {
325  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
326
327  // Send the unique device ID to the Java audio manager and make the
328  // device switch. Provide an empty string to the Java audio manager
329  // if the default device is selected.
330  JNIEnv* env = AttachCurrentThread();
331  ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString(
332      env,
333      device_id == AudioManagerBase::kDefaultDeviceId ?
334          std::string() : device_id);
335  return Java_AudioManagerAndroid_setDevice(
336      env, j_audio_manager_.obj(), j_device_id.obj());
337}
338
339int AudioManagerAndroid::GetNativeOutputSampleRate() {
340  return Java_AudioManagerAndroid_getNativeOutputSampleRate(
341      base::android::AttachCurrentThread(),
342      j_audio_manager_.obj());
343}
344
345bool AudioManagerAndroid::IsAudioLowLatencySupported() {
346  return Java_AudioManagerAndroid_isAudioLowLatencySupported(
347      base::android::AttachCurrentThread(),
348      j_audio_manager_.obj());
349}
350
351int AudioManagerAndroid::GetAudioLowLatencyOutputFrameSize() {
352  return Java_AudioManagerAndroid_getAudioLowLatencyOutputFrameSize(
353      base::android::AttachCurrentThread(),
354      j_audio_manager_.obj());
355}
356
357int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate,
358                                                   int channels) {
359  if (IsAudioLowLatencySupported())
360    return GetAudioLowLatencyOutputFrameSize();
361
362  return std::max(kDefaultOutputBufferSize,
363                  Java_AudioManagerAndroid_getMinOutputFrameSize(
364                      base::android::AttachCurrentThread(),
365                      sample_rate, channels));
366}
367
368void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
369  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
370  for (OutputStreams::iterator it = streams_.begin();
371       it != streams_.end(); ++it) {
372    (*it)->SetMute(muted);
373  }
374}
375
376}  // namespace media
377