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/strings/string_number_conversions.h"
13#include "jni/AudioManagerAndroid_jni.h"
14#include "media/audio/android/audio_record_input.h"
15#include "media/audio/android/opensles_input.h"
16#include "media/audio/android/opensles_output.h"
17#include "media/audio/audio_manager.h"
18#include "media/audio/audio_parameters.h"
19#include "media/audio/fake_audio_input_stream.h"
20#include "media/base/channel_layout.h"
21
22using base::android::AppendJavaStringArrayToStringVector;
23using base::android::AttachCurrentThread;
24using base::android::ConvertJavaStringToUTF8;
25using base::android::ConvertUTF8ToJavaString;
26using base::android::ScopedJavaLocalRef;
27
28namespace media {
29
30static void AddDefaultDevice(AudioDeviceNames* device_names) {
31  DCHECK(device_names->empty());
32  device_names->push_front(
33      AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
34                      AudioManagerBase::kDefaultDeviceId));
35}
36
37// Maximum number of output streams that can be open simultaneously.
38static const int kMaxOutputStreams = 10;
39
40static const int kAudioModeNormal = 0x00000000;
41static const int kAudioModeInCommunication = 0x00000003;
42
43static const int kDefaultInputBufferSize = 1024;
44static const int kDefaultOutputBufferSize = 2048;
45
46AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
47  return new AudioManagerAndroid(audio_log_factory);
48}
49
50AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory)
51    : AudioManagerBase(audio_log_factory) {
52  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
53
54  j_audio_manager_.Reset(
55      Java_AudioManagerAndroid_createAudioManagerAndroid(
56          base::android::AttachCurrentThread(),
57          base::android::GetApplicationContext(),
58          reinterpret_cast<intptr_t>(this)));
59  Init();
60}
61
62AudioManagerAndroid::~AudioManagerAndroid() {
63  Close();
64  Shutdown();
65}
66
67bool AudioManagerAndroid::HasAudioOutputDevices() {
68  return true;
69}
70
71bool AudioManagerAndroid::HasAudioInputDevices() {
72  return true;
73}
74
75void AudioManagerAndroid::GetAudioInputDeviceNames(
76    AudioDeviceNames* device_names) {
77  // Always add default device parameters as first element.
78  AddDefaultDevice(device_names);
79
80  JNIEnv* env = AttachCurrentThread();
81  ScopedJavaLocalRef<jobjectArray> j_device_array =
82      Java_AudioManagerAndroid_getAudioInputDeviceNames(
83          env, j_audio_manager_.obj());
84  jsize len = env->GetArrayLength(j_device_array.obj());
85  AudioDeviceName device;
86  for (jsize i = 0; i < len; ++i) {
87    ScopedJavaLocalRef<jobject> j_device(
88        env, env->GetObjectArrayElement(j_device_array.obj(), i));
89    ScopedJavaLocalRef<jstring> j_device_name =
90        Java_AudioDeviceName_name(env, j_device.obj());
91    ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name);
92    ScopedJavaLocalRef<jstring> j_device_id =
93        Java_AudioDeviceName_id(env, j_device.obj());
94    ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id);
95    device_names->push_back(device);
96  }
97}
98
99void AudioManagerAndroid::GetAudioOutputDeviceNames(
100    AudioDeviceNames* device_names) {
101  // TODO(henrika): enumerate using GetAudioInputDeviceNames().
102  AddDefaultDevice(device_names);
103}
104
105AudioParameters AudioManagerAndroid::GetInputStreamParameters(
106    const std::string& device_id) {
107  JNIEnv* env = AttachCurrentThread();
108  // Use mono as preferred number of input channels on Android to save
109  // resources. Using mono also avoids a driver issue seen on Samsung
110  // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details.
111  ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO;
112  int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize(
113      env, GetNativeOutputSampleRate(),
114      ChannelLayoutToChannelCount(channel_layout));
115  int effects = AudioParameters::NO_EFFECTS;
116  effects |= Java_AudioManagerAndroid_shouldUseAcousticEchoCanceler(env) ?
117      AudioParameters::ECHO_CANCELLER : AudioParameters::NO_EFFECTS;
118  AudioParameters params(
119      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 0,
120      GetNativeOutputSampleRate(), 16,
121      buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size, effects);
122  return params;
123}
124
125AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
126    const AudioParameters& params,
127    const std::string& device_id,
128    const std::string& input_device_id) {
129  AudioOutputStream* stream =
130      AudioManagerBase::MakeAudioOutputStream(params, std::string(),
131          std::string());
132  if (stream && output_stream_count() == 1) {
133    SetAudioMode(kAudioModeInCommunication);
134  }
135
136  {
137    base::AutoLock lock(streams_lock_);
138    streams_.insert(static_cast<OpenSLESOutputStream*>(stream));
139  }
140
141  return stream;
142}
143
144AudioInputStream* AudioManagerAndroid::MakeAudioInputStream(
145    const AudioParameters& params, const std::string& device_id) {
146  AudioInputStream* stream =
147      AudioManagerBase::MakeAudioInputStream(params, device_id);
148  return stream;
149}
150
151void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) {
152  AudioManagerBase::ReleaseOutputStream(stream);
153  if (!output_stream_count()) {
154    SetAudioMode(kAudioModeNormal);
155  }
156  base::AutoLock lock(streams_lock_);
157  streams_.erase(static_cast<OpenSLESOutputStream*>(stream));
158}
159
160void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) {
161  AudioManagerBase::ReleaseInputStream(stream);
162}
163
164AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream(
165    const AudioParameters& params) {
166  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
167  return new OpenSLESOutputStream(this, params);
168}
169
170AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
171    const AudioParameters& params,
172    const std::string& device_id,
173    const std::string& input_device_id) {
174  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
175  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
176  return new OpenSLESOutputStream(this, params);
177}
178
179AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
180    const AudioParameters& params, const std::string& device_id) {
181  // TODO(henrika): add support for device selection if/when any client
182  // needs it.
183  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
184  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
185  return new OpenSLESInputStream(this, params);
186}
187
188AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream(
189    const AudioParameters& params, const std::string& device_id) {
190  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
191  DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!";
192  // Utilize the device ID to select the correct input device.
193  // Note that the input device is always associated with a certain output
194  // device, i.e., this selection does also switch the output device.
195  // All input and output streams will be affected by the device selection.
196  SetAudioDevice(device_id);
197
198  if (params.effects() != AudioParameters::NO_EFFECTS) {
199    // Platform effects can only be enabled through the AudioRecord path.
200    // An effect should only have been requested here if recommended by
201    // AudioManagerAndroid.shouldUse<Effect>.
202    //
203    // Creating this class requires Jelly Bean, which is already guaranteed by
204    // shouldUse<Effect>. Only DCHECK on that condition to allow tests to use
205    // the effect settings as a way to select the input path.
206    DCHECK_GE(base::android::BuildInfo::GetInstance()->sdk_int(), 16);
207    DVLOG(1) << "Creating AudioRecordInputStream";
208    return new AudioRecordInputStream(this, params);
209  }
210  DVLOG(1) << "Creating OpenSLESInputStream";
211  return new OpenSLESInputStream(this, params);
212}
213
214int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate,
215                                                   int channels) {
216  if (IsAudioLowLatencySupported()) {
217    return GetAudioLowLatencyOutputFrameSize();
218  } else {
219    return std::max(kDefaultOutputBufferSize,
220                    Java_AudioManagerAndroid_getMinOutputFrameSize(
221                        base::android::AttachCurrentThread(),
222                        sample_rate, channels));
223  }
224}
225
226AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters(
227    const std::string& output_device_id,
228    const AudioParameters& input_params) {
229  // TODO(tommi): Support |output_device_id|.
230  DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
231  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
232  int sample_rate = GetNativeOutputSampleRate();
233  int buffer_size = GetOptimalOutputFrameSize(sample_rate, 2);
234  int bits_per_sample = 16;
235  int input_channels = 0;
236  if (input_params.IsValid()) {
237    // Use the client's input parameters if they are valid.
238    sample_rate = input_params.sample_rate();
239    bits_per_sample = input_params.bits_per_sample();
240    channel_layout = input_params.channel_layout();
241    input_channels = input_params.input_channels();
242    buffer_size = GetOptimalOutputFrameSize(
243        sample_rate, ChannelLayoutToChannelCount(channel_layout));
244  }
245
246  int user_buffer_size = GetUserBufferSize();
247  if (user_buffer_size)
248    buffer_size = user_buffer_size;
249
250  return AudioParameters(
251      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, input_channels,
252      sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
253}
254
255// static
256bool AudioManagerAndroid::RegisterAudioManager(JNIEnv* env) {
257  return RegisterNativesImpl(env);
258}
259
260void AudioManagerAndroid::Init() {
261  Java_AudioManagerAndroid_init(
262      base::android::AttachCurrentThread(),
263      j_audio_manager_.obj());
264}
265
266void AudioManagerAndroid::Close() {
267  Java_AudioManagerAndroid_close(
268      base::android::AttachCurrentThread(),
269      j_audio_manager_.obj());
270}
271
272void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) {
273  GetMessageLoop()->PostTask(
274      FROM_HERE,
275      base::Bind(
276          &AudioManagerAndroid::DoSetMuteOnAudioThread,
277          base::Unretained(this),
278          muted));
279}
280
281void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
282  base::AutoLock lock(streams_lock_);
283  for (OutputStreams::iterator it = streams_.begin();
284       it != streams_.end(); ++it) {
285    (*it)->SetMute(muted);
286  }
287}
288
289void AudioManagerAndroid::SetAudioMode(int mode) {
290  Java_AudioManagerAndroid_setMode(
291      base::android::AttachCurrentThread(),
292      j_audio_manager_.obj(), mode);
293}
294
295void AudioManagerAndroid::SetAudioDevice(const std::string& device_id) {
296  JNIEnv* env = AttachCurrentThread();
297
298  // Send the unique device ID to the Java audio manager and make the
299  // device switch. Provide an empty string to the Java audio manager
300  // if the default device is selected.
301  ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString(
302      env,
303      device_id == AudioManagerBase::kDefaultDeviceId ?
304          std::string() : device_id);
305  Java_AudioManagerAndroid_setDevice(
306      env, j_audio_manager_.obj(), j_device_id.obj());
307}
308
309int AudioManagerAndroid::GetNativeOutputSampleRate() {
310  return Java_AudioManagerAndroid_getNativeOutputSampleRate(
311      base::android::AttachCurrentThread(),
312      j_audio_manager_.obj());
313}
314
315bool AudioManagerAndroid::IsAudioLowLatencySupported() {
316  return Java_AudioManagerAndroid_isAudioLowLatencySupported(
317      base::android::AttachCurrentThread(),
318      j_audio_manager_.obj());
319}
320
321int AudioManagerAndroid::GetAudioLowLatencyOutputFrameSize() {
322  return Java_AudioManagerAndroid_getAudioLowLatencyOutputFrameSize(
323      base::android::AttachCurrentThread(),
324      j_audio_manager_.obj());
325}
326
327}  // namespace media
328