Home | History | Annotate | Download | only in audio
      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 
     19 namespace media {
     20 
     21 static const int kStreamCloseDelaySeconds = 5;
     22 
     23 // Default maximum number of output streams that can be open simultaneously
     24 // for all platforms.
     25 static const int kDefaultMaxOutputStreams = 16;
     26 
     27 // Default maximum number of input streams that can be open simultaneously
     28 // for all platforms.
     29 static const int kDefaultMaxInputStreams = 16;
     30 
     31 static const int kMaxInputChannels = 3;
     32 
     33 const char AudioManagerBase::kDefaultDeviceName[] = "Default";
     34 const char AudioManagerBase::kDefaultDeviceId[] = "default";
     35 const char AudioManagerBase::kLoopbackInputDeviceId[] = "loopback";
     36 
     37 struct 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 
     55 class 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 
     74 AudioManagerBase::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 
    102 AudioManagerBase::~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 
    115 base::string16 AudioManagerBase::GetAudioInputDeviceModel() {
    116   return base::string16();
    117 }
    118 
    119 scoped_refptr<base::SingleThreadTaskRunner> AudioManagerBase::GetTaskRunner() {
    120   return task_runner_;
    121 }
    122 
    123 scoped_refptr<base::SingleThreadTaskRunner>
    124 AudioManagerBase::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 
    132 AudioOutputStream* 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 
    181 AudioInputStream* 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 
    224 AudioOutputStream* 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 
    294 void AudioManagerBase::ShowAudioInputSettings() {
    295 }
    296 
    297 void AudioManagerBase::GetAudioInputDeviceNames(
    298     AudioDeviceNames* device_names) {
    299 }
    300 
    301 void AudioManagerBase::GetAudioOutputDeviceNames(
    302     AudioDeviceNames* device_names) {
    303 }
    304 
    305 void 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 
    314 void 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 
    321 void 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 
    335 void 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 
    343 void AudioManagerBase::AddOutputDeviceChangeListener(
    344     AudioDeviceListener* listener) {
    345   DCHECK(task_runner_->BelongsToCurrentThread());
    346   output_listeners_.AddObserver(listener);
    347 }
    348 
    349 void AudioManagerBase::RemoveOutputDeviceChangeListener(
    350     AudioDeviceListener* listener) {
    351   DCHECK(task_runner_->BelongsToCurrentThread());
    352   output_listeners_.RemoveObserver(listener);
    353 }
    354 
    355 void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() {
    356   DCHECK(task_runner_->BelongsToCurrentThread());
    357   DVLOG(1) << "Firing OnDeviceChange() notifications.";
    358   FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange());
    359 }
    360 
    361 AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() {
    362   return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(),
    363       AudioParameters());
    364 }
    365 
    366 AudioParameters AudioManagerBase::GetOutputStreamParameters(
    367     const std::string& device_id) {
    368   return GetPreferredOutputStreamParameters(device_id,
    369       AudioParameters());
    370 }
    371 
    372 AudioParameters AudioManagerBase::GetInputStreamParameters(
    373     const std::string& device_id) {
    374   NOTREACHED();
    375   return AudioParameters();
    376 }
    377 
    378 std::string AudioManagerBase::GetAssociatedOutputDeviceID(
    379     const std::string& input_device_id) {
    380   return "";
    381 }
    382 
    383 std::string AudioManagerBase::GetDefaultOutputDeviceID() {
    384   return "";
    385 }
    386 
    387 int 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 
    398 scoped_ptr<AudioLog> AudioManagerBase::CreateAudioLog(
    399     AudioLogFactory::AudioComponent component) {
    400   return audio_log_factory_->CreateAudioLog(component);
    401 }
    402 
    403 void AudioManagerBase::SetHasKeyboardMic() {
    404   NOTREACHED();
    405 }
    406 
    407 }  // namespace media
    408