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