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/audio_manager_base.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/command_line.h"
10#include "base/strings/string_number_conversions.h"
11#include "build/build_config.h"
12#include "media/audio/audio_output_dispatcher_impl.h"
13#include "media/audio/audio_output_proxy.h"
14#include "media/audio/audio_output_resampler.h"
15#include "media/audio/fake_audio_input_stream.h"
16#include "media/audio/fake_audio_output_stream.h"
17#include "media/base/media_switches.h"
18
19namespace media {
20
21static const int kStreamCloseDelaySeconds = 5;
22
23// Default maximum number of output streams that can be open simultaneously
24// for all platforms.
25static const int kDefaultMaxOutputStreams = 16;
26
27// Default maximum number of input streams that can be open simultaneously
28// for all platforms.
29static const int kDefaultMaxInputStreams = 16;
30
31static const int kMaxInputChannels = 3;
32
33const char AudioManagerBase::kDefaultDeviceName[] = "Default";
34const char AudioManagerBase::kDefaultDeviceId[] = "default";
35const char AudioManagerBase::kLoopbackInputDeviceId[] = "loopback";
36
37struct AudioManagerBase::DispatcherParams {
38  DispatcherParams(const AudioParameters& input,
39                   const AudioParameters& output,
40                   const std::string& output_device_id)
41      : input_params(input),
42        output_params(output),
43        output_device_id(output_device_id) {}
44  ~DispatcherParams() {}
45
46  const AudioParameters input_params;
47  const AudioParameters output_params;
48  const std::string output_device_id;
49  scoped_refptr<AudioOutputDispatcher> dispatcher;
50
51 private:
52  DISALLOW_COPY_AND_ASSIGN(DispatcherParams);
53};
54
55class AudioManagerBase::CompareByParams {
56 public:
57  explicit CompareByParams(const DispatcherParams* dispatcher)
58      : dispatcher_(dispatcher) {}
59  bool operator()(DispatcherParams* dispatcher_in) const {
60    // We will reuse the existing dispatcher when:
61    // 1) Unified IO is not used, input_params and output_params of the
62    //    existing dispatcher are the same as the requested dispatcher.
63    // 2) Unified IO is used, input_params and output_params of the existing
64    //    dispatcher are the same as the request dispatcher.
65    return (dispatcher_->input_params.Equals(dispatcher_in->input_params) &&
66            dispatcher_->output_params.Equals(dispatcher_in->output_params) &&
67            dispatcher_->output_device_id == dispatcher_in->output_device_id);
68  }
69
70 private:
71  const DispatcherParams* dispatcher_;
72};
73
74AudioManagerBase::AudioManagerBase(AudioLogFactory* audio_log_factory)
75    : max_num_output_streams_(kDefaultMaxOutputStreams),
76      max_num_input_streams_(kDefaultMaxInputStreams),
77      num_output_streams_(0),
78      num_input_streams_(0),
79      // TODO(dalecurtis): Switch this to an ObserverListThreadSafe, so we don't
80      // block the UI thread when swapping devices.
81      output_listeners_(
82          ObserverList<AudioDeviceListener>::NOTIFY_EXISTING_ONLY),
83      audio_thread_("AudioThread"),
84      audio_log_factory_(audio_log_factory) {
85#if defined(OS_WIN)
86  audio_thread_.init_com_with_mta(true);
87#elif defined(OS_MACOSX)
88  // CoreAudio calls must occur on the main thread of the process, which in our
89  // case is sadly the browser UI thread.  Failure to execute calls on the right
90  // thread leads to crashes and odd behavior.  See http://crbug.com/158170.
91  // TODO(dalecurtis): We should require the message loop to be passed in.
92  if (base::MessageLoopForUI::IsCurrent()) {
93    task_runner_ = base::MessageLoopProxy::current();
94    return;
95  }
96#endif
97
98  CHECK(audio_thread_.Start());
99  task_runner_ = audio_thread_.message_loop_proxy();
100}
101
102AudioManagerBase::~AudioManagerBase() {
103  // The platform specific AudioManager implementation must have already
104  // stopped the audio thread. Otherwise, we may destroy audio streams before
105  // stopping the thread, resulting an unexpected behavior.
106  // This way we make sure activities of the audio streams are all stopped
107  // before we destroy them.
108  CHECK(!audio_thread_.IsRunning());
109  // All the output streams should have been deleted.
110  DCHECK_EQ(0, num_output_streams_);
111  // All the input streams should have been deleted.
112  DCHECK_EQ(0, num_input_streams_);
113}
114
115base::string16 AudioManagerBase::GetAudioInputDeviceModel() {
116  return base::string16();
117}
118
119scoped_refptr<base::SingleThreadTaskRunner> AudioManagerBase::GetTaskRunner() {
120  return task_runner_;
121}
122
123scoped_refptr<base::SingleThreadTaskRunner>
124AudioManagerBase::GetWorkerTaskRunner() {
125  // Lazily start the worker thread.
126  if (!audio_thread_.IsRunning())
127    CHECK(audio_thread_.Start());
128
129  return audio_thread_.message_loop_proxy();
130}
131
132AudioOutputStream* AudioManagerBase::MakeAudioOutputStream(
133    const AudioParameters& params,
134    const std::string& device_id) {
135  // TODO(miu): Fix ~50 call points across several unit test modules to call
136  // this method on the audio thread, then uncomment the following:
137  // DCHECK(task_runner_->BelongsToCurrentThread());
138
139  if (!params.IsValid()) {
140    DLOG(ERROR) << "Audio parameters are invalid";
141    return NULL;
142  }
143
144  // Limit the number of audio streams opened. This is to prevent using
145  // excessive resources for a large number of audio streams. More
146  // importantly it prevents instability on certain systems.
147  // See bug: http://crbug.com/30242.
148  if (num_output_streams_ >= max_num_output_streams_) {
149    DLOG(ERROR) << "Number of opened output audio streams "
150                << num_output_streams_
151                << " exceed the max allowed number "
152                << max_num_output_streams_;
153    return NULL;
154  }
155
156  AudioOutputStream* stream;
157  switch (params.format()) {
158    case AudioParameters::AUDIO_PCM_LINEAR:
159      DCHECK(device_id.empty())
160          << "AUDIO_PCM_LINEAR supports only the default device.";
161      stream = MakeLinearOutputStream(params);
162      break;
163    case AudioParameters::AUDIO_PCM_LOW_LATENCY:
164      stream = MakeLowLatencyOutputStream(params, device_id);
165      break;
166    case AudioParameters::AUDIO_FAKE:
167      stream = FakeAudioOutputStream::MakeFakeStream(this, params);
168      break;
169    default:
170      stream = NULL;
171      break;
172  }
173
174  if (stream) {
175    ++num_output_streams_;
176  }
177
178  return stream;
179}
180
181AudioInputStream* AudioManagerBase::MakeAudioInputStream(
182    const AudioParameters& params,
183    const std::string& device_id) {
184  // TODO(miu): Fix ~20 call points across several unit test modules to call
185  // this method on the audio thread, then uncomment the following:
186  // DCHECK(task_runner_->BelongsToCurrentThread());
187
188  if (!params.IsValid() || (params.channels() > kMaxInputChannels) ||
189      device_id.empty()) {
190    DLOG(ERROR) << "Audio parameters are invalid for device " << device_id;
191    return NULL;
192  }
193
194  if (num_input_streams_ >= max_num_input_streams_) {
195    DLOG(ERROR) << "Number of opened input audio streams "
196                << num_input_streams_
197                << " exceed the max allowed number " << max_num_input_streams_;
198    return NULL;
199  }
200
201  AudioInputStream* stream;
202  switch (params.format()) {
203    case AudioParameters::AUDIO_PCM_LINEAR:
204      stream = MakeLinearInputStream(params, device_id);
205      break;
206    case AudioParameters::AUDIO_PCM_LOW_LATENCY:
207      stream = MakeLowLatencyInputStream(params, device_id);
208      break;
209    case AudioParameters::AUDIO_FAKE:
210      stream = FakeAudioInputStream::MakeFakeStream(this, params);
211      break;
212    default:
213      stream = NULL;
214      break;
215  }
216
217  if (stream) {
218    ++num_input_streams_;
219  }
220
221  return stream;
222}
223
224AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy(
225    const AudioParameters& params,
226    const std::string& device_id) {
227  DCHECK(task_runner_->BelongsToCurrentThread());
228
229  // If the caller supplied an empty device id to select the default device,
230  // we fetch the actual device id of the default device so that the lookup
231  // will find the correct device regardless of whether it was opened as
232  // "default" or via the specific id.
233  // NOTE: Implementations that don't yet support opening non-default output
234  // devices may return an empty string from GetDefaultOutputDeviceID().
235  std::string output_device_id = device_id.empty() ?
236      GetDefaultOutputDeviceID() : device_id;
237
238  // If we're not using AudioOutputResampler our output parameters are the same
239  // as our input parameters.
240  AudioParameters output_params = params;
241  if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
242    output_params =
243        GetPreferredOutputStreamParameters(output_device_id, params);
244
245    // Ensure we only pass on valid output parameters.
246    if (!output_params.IsValid()) {
247      // We've received invalid audio output parameters, so switch to a mock
248      // output device based on the input parameters.  This may happen if the OS
249      // provided us junk values for the hardware configuration.
250      LOG(ERROR) << "Invalid audio output parameters received; using fake "
251                 << "audio path. Channels: " << output_params.channels() << ", "
252                 << "Sample Rate: " << output_params.sample_rate() << ", "
253                 << "Bits Per Sample: " << output_params.bits_per_sample()
254                 << ", Frames Per Buffer: "
255                 << output_params.frames_per_buffer();
256
257      // Tell the AudioManager to create a fake output device.
258      output_params = AudioParameters(
259          AudioParameters::AUDIO_FAKE, params.channel_layout(),
260          params.sample_rate(), params.bits_per_sample(),
261          params.frames_per_buffer());
262    }
263  }
264
265  DispatcherParams* dispatcher_params =
266      new DispatcherParams(params, output_params, output_device_id);
267
268  AudioOutputDispatchers::iterator it =
269      std::find_if(output_dispatchers_.begin(), output_dispatchers_.end(),
270                   CompareByParams(dispatcher_params));
271  if (it != output_dispatchers_.end()) {
272    delete dispatcher_params;
273    return new AudioOutputProxy((*it)->dispatcher.get());
274  }
275
276  const base::TimeDelta kCloseDelay =
277      base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds);
278  scoped_refptr<AudioOutputDispatcher> dispatcher;
279  if (output_params.format() != AudioParameters::AUDIO_FAKE) {
280    dispatcher = new AudioOutputResampler(this, params, output_params,
281                                          output_device_id,
282                                          kCloseDelay);
283  } else {
284    dispatcher = new AudioOutputDispatcherImpl(this, output_params,
285                                               output_device_id,
286                                               kCloseDelay);
287  }
288
289  dispatcher_params->dispatcher = dispatcher;
290  output_dispatchers_.push_back(dispatcher_params);
291  return new AudioOutputProxy(dispatcher.get());
292}
293
294void AudioManagerBase::ShowAudioInputSettings() {
295}
296
297void AudioManagerBase::GetAudioInputDeviceNames(
298    AudioDeviceNames* device_names) {
299}
300
301void AudioManagerBase::GetAudioOutputDeviceNames(
302    AudioDeviceNames* device_names) {
303}
304
305void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) {
306  DCHECK(stream);
307  // TODO(xians) : Have a clearer destruction path for the AudioOutputStream.
308  // For example, pass the ownership to AudioManager so it can delete the
309  // streams.
310  --num_output_streams_;
311  delete stream;
312}
313
314void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) {
315  DCHECK(stream);
316  // TODO(xians) : Have a clearer destruction path for the AudioInputStream.
317  --num_input_streams_;
318  delete stream;
319}
320
321void AudioManagerBase::Shutdown() {
322  // Only true when we're sharing the UI message loop with the browser.  The UI
323  // loop is no longer running at this time and browser destruction is imminent.
324  if (task_runner_->BelongsToCurrentThread()) {
325    ShutdownOnAudioThread();
326  } else {
327    task_runner_->PostTask(FROM_HERE, base::Bind(
328        &AudioManagerBase::ShutdownOnAudioThread, base::Unretained(this)));
329  }
330
331  // Stop() will wait for any posted messages to be processed first.
332  audio_thread_.Stop();
333}
334
335void AudioManagerBase::ShutdownOnAudioThread() {
336  DCHECK(task_runner_->BelongsToCurrentThread());
337  while (!output_dispatchers_.empty()) {
338    output_dispatchers_.back()->dispatcher->Shutdown();
339    output_dispatchers_.pop_back();
340  }
341}
342
343void AudioManagerBase::AddOutputDeviceChangeListener(
344    AudioDeviceListener* listener) {
345  DCHECK(task_runner_->BelongsToCurrentThread());
346  output_listeners_.AddObserver(listener);
347}
348
349void AudioManagerBase::RemoveOutputDeviceChangeListener(
350    AudioDeviceListener* listener) {
351  DCHECK(task_runner_->BelongsToCurrentThread());
352  output_listeners_.RemoveObserver(listener);
353}
354
355void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() {
356  DCHECK(task_runner_->BelongsToCurrentThread());
357  DVLOG(1) << "Firing OnDeviceChange() notifications.";
358  FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange());
359}
360
361AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() {
362  return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(),
363      AudioParameters());
364}
365
366AudioParameters AudioManagerBase::GetOutputStreamParameters(
367    const std::string& device_id) {
368  return GetPreferredOutputStreamParameters(device_id,
369      AudioParameters());
370}
371
372AudioParameters AudioManagerBase::GetInputStreamParameters(
373    const std::string& device_id) {
374  NOTREACHED();
375  return AudioParameters();
376}
377
378std::string AudioManagerBase::GetAssociatedOutputDeviceID(
379    const std::string& input_device_id) {
380  return "";
381}
382
383std::string AudioManagerBase::GetDefaultOutputDeviceID() {
384  return "";
385}
386
387int AudioManagerBase::GetUserBufferSize() {
388  const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
389  int buffer_size = 0;
390  std::string buffer_size_str(cmd_line->GetSwitchValueASCII(
391      switches::kAudioBufferSize));
392  if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0)
393    return buffer_size;
394
395  return 0;
396}
397
398scoped_ptr<AudioLog> AudioManagerBase::CreateAudioLog(
399    AudioLogFactory::AudioComponent component) {
400  return audio_log_factory_->CreateAudioLog(component);
401}
402
403void AudioManagerBase::SetHasKeyboardMic() {
404  NOTREACHED();
405}
406
407}  // namespace media
408