audio_manager_base.cc revision f2477e01787aa58f445919b809d89e252beef54f
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() 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#if defined(OS_WIN) 91 audio_thread_.init_com_with_mta(true); 92#elif defined(OS_MACOSX) 93 // CoreAudio calls must occur on the main thread of the process, which in our 94 // case is sadly the browser UI thread. Failure to execute calls on the right 95 // thread leads to crashes and odd behavior. See http://crbug.com/158170. 96 // TODO(dalecurtis): We should require the message loop to be passed in. 97 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 98 if (!cmd_line->HasSwitch(switches::kDisableMainThreadAudio) && 99 base::MessageLoopProxy::current().get() && 100 base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI)) { 101 message_loop_ = base::MessageLoopProxy::current(); 102 return; 103 } 104#endif 105 106 CHECK(audio_thread_.Start()); 107 message_loop_ = audio_thread_.message_loop_proxy(); 108} 109 110AudioManagerBase::~AudioManagerBase() { 111 // The platform specific AudioManager implementation must have already 112 // stopped the audio thread. Otherwise, we may destroy audio streams before 113 // stopping the thread, resulting an unexpected behavior. 114 // This way we make sure activities of the audio streams are all stopped 115 // before we destroy them. 116 CHECK(!audio_thread_.IsRunning()); 117 // All the output streams should have been deleted. 118 DCHECK_EQ(0, num_output_streams_); 119 // All the input streams should have been deleted. 120 DCHECK_EQ(0, num_input_streams_); 121} 122 123string16 AudioManagerBase::GetAudioInputDeviceModel() { 124 return string16(); 125} 126 127scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() { 128 return message_loop_; 129} 130 131scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetWorkerLoop() { 132 // Lazily start the worker thread. 133 if (!audio_thread_.IsRunning()) 134 CHECK(audio_thread_.Start()); 135 136 return audio_thread_.message_loop_proxy(); 137} 138 139AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( 140 const AudioParameters& params, 141 const std::string& device_id, 142 const std::string& input_device_id) { 143 // TODO(miu): Fix ~50 call points across several unit test modules to call 144 // this method on the audio thread, then uncomment the following: 145 // DCHECK(message_loop_->BelongsToCurrentThread()); 146 147 if (!params.IsValid()) { 148 DLOG(ERROR) << "Audio parameters are invalid"; 149 return NULL; 150 } 151 152 // Limit the number of audio streams opened. This is to prevent using 153 // excessive resources for a large number of audio streams. More 154 // importantly it prevents instability on certain systems. 155 // See bug: http://crbug.com/30242. 156 if (num_output_streams_ >= max_num_output_streams_) { 157 DLOG(ERROR) << "Number of opened output audio streams " 158 << num_output_streams_ 159 << " exceed the max allowed number " 160 << max_num_output_streams_; 161 return NULL; 162 } 163 164 AudioOutputStream* stream; 165 switch (params.format()) { 166 case AudioParameters::AUDIO_PCM_LINEAR: 167 DCHECK(device_id.empty()) 168 << "AUDIO_PCM_LINEAR supports only the default device."; 169 stream = MakeLinearOutputStream(params); 170 break; 171 case AudioParameters::AUDIO_PCM_LOW_LATENCY: 172 stream = MakeLowLatencyOutputStream(params, device_id, input_device_id); 173 break; 174 case AudioParameters::AUDIO_FAKE: 175 stream = FakeAudioOutputStream::MakeFakeStream(this, params); 176 break; 177 default: 178 stream = NULL; 179 break; 180 } 181 182 if (stream) { 183 ++num_output_streams_; 184 } 185 186 return stream; 187} 188 189AudioInputStream* AudioManagerBase::MakeAudioInputStream( 190 const AudioParameters& params, 191 const std::string& device_id) { 192 // TODO(miu): Fix ~20 call points across several unit test modules to call 193 // this method on the audio thread, then uncomment the following: 194 // DCHECK(message_loop_->BelongsToCurrentThread()); 195 196 if (!params.IsValid() || (params.channels() > kMaxInputChannels) || 197 device_id.empty()) { 198 DLOG(ERROR) << "Audio parameters are invalid for device " << device_id; 199 return NULL; 200 } 201 202 if (num_input_streams_ >= max_num_input_streams_) { 203 DLOG(ERROR) << "Number of opened input audio streams " 204 << num_input_streams_ 205 << " exceed the max allowed number " << max_num_input_streams_; 206 return NULL; 207 } 208 209 AudioInputStream* stream; 210 switch (params.format()) { 211 case AudioParameters::AUDIO_PCM_LINEAR: 212 stream = MakeLinearInputStream(params, device_id); 213 break; 214 case AudioParameters::AUDIO_PCM_LOW_LATENCY: 215 stream = MakeLowLatencyInputStream(params, device_id); 216 break; 217 case AudioParameters::AUDIO_FAKE: 218 stream = FakeAudioInputStream::MakeFakeStream(this, params); 219 break; 220 default: 221 stream = NULL; 222 break; 223 } 224 225 if (stream) { 226 ++num_input_streams_; 227 } 228 229 return stream; 230} 231 232AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( 233 const AudioParameters& params, 234 const std::string& device_id, 235 const std::string& input_device_id) { 236 DCHECK(message_loop_->BelongsToCurrentThread()); 237 238 // If the caller supplied an empty device id to select the default device, 239 // we fetch the actual device id of the default device so that the lookup 240 // will find the correct device regardless of whether it was opened as 241 // "default" or via the specific id. 242 // NOTE: Implementations that don't yet support opening non-default output 243 // devices may return an empty string from GetDefaultOutputDeviceID(). 244 std::string output_device_id = device_id.empty() ? 245 GetDefaultOutputDeviceID() : device_id; 246 247 // If we're not using AudioOutputResampler our output parameters are the same 248 // as our input parameters. 249 AudioParameters output_params = params; 250 if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) { 251 output_params = 252 GetPreferredOutputStreamParameters(output_device_id, params); 253 254 // Ensure we only pass on valid output parameters. 255 if (!output_params.IsValid()) { 256 // We've received invalid audio output parameters, so switch to a mock 257 // output device based on the input parameters. This may happen if the OS 258 // provided us junk values for the hardware configuration. 259 LOG(ERROR) << "Invalid audio output parameters received; using fake " 260 << "audio path. Channels: " << output_params.channels() << ", " 261 << "Sample Rate: " << output_params.sample_rate() << ", " 262 << "Bits Per Sample: " << output_params.bits_per_sample() 263 << ", Frames Per Buffer: " 264 << output_params.frames_per_buffer(); 265 266 // Tell the AudioManager to create a fake output device. 267 output_params = AudioParameters( 268 AudioParameters::AUDIO_FAKE, params.channel_layout(), 269 params.sample_rate(), params.bits_per_sample(), 270 params.frames_per_buffer()); 271 } 272 } 273 274 DispatcherParams* dispatcher_params = 275 new DispatcherParams(params, output_params, output_device_id, 276 input_device_id); 277 278 AudioOutputDispatchers::iterator it = 279 std::find_if(output_dispatchers_.begin(), output_dispatchers_.end(), 280 CompareByParams(dispatcher_params)); 281 if (it != output_dispatchers_.end()) { 282 delete dispatcher_params; 283 return new AudioOutputProxy((*it)->dispatcher.get()); 284 } 285 286 const base::TimeDelta kCloseDelay = 287 base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds); 288 scoped_refptr<AudioOutputDispatcher> dispatcher; 289 if (output_params.format() != AudioParameters::AUDIO_FAKE) { 290 dispatcher = new AudioOutputResampler(this, params, output_params, 291 output_device_id, input_device_id, 292 kCloseDelay); 293 } else { 294 dispatcher = new AudioOutputDispatcherImpl(this, output_params, 295 output_device_id, 296 input_device_id, kCloseDelay); 297 } 298 299 dispatcher_params->dispatcher = dispatcher; 300 output_dispatchers_.push_back(dispatcher_params); 301 return new AudioOutputProxy(dispatcher.get()); 302} 303 304void AudioManagerBase::ShowAudioInputSettings() { 305} 306 307void AudioManagerBase::GetAudioInputDeviceNames( 308 AudioDeviceNames* device_names) { 309} 310 311void AudioManagerBase::GetAudioOutputDeviceNames( 312 AudioDeviceNames* device_names) { 313} 314 315void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) { 316 DCHECK(stream); 317 // TODO(xians) : Have a clearer destruction path for the AudioOutputStream. 318 // For example, pass the ownership to AudioManager so it can delete the 319 // streams. 320 --num_output_streams_; 321 delete stream; 322} 323 324void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) { 325 DCHECK(stream); 326 // TODO(xians) : Have a clearer destruction path for the AudioInputStream. 327 --num_input_streams_; 328 delete stream; 329} 330 331void AudioManagerBase::Shutdown() { 332 // Only true when we're sharing the UI message loop with the browser. The UI 333 // loop is no longer running at this time and browser destruction is imminent. 334 if (message_loop_->BelongsToCurrentThread()) { 335 ShutdownOnAudioThread(); 336 } else { 337 message_loop_->PostTask(FROM_HERE, base::Bind( 338 &AudioManagerBase::ShutdownOnAudioThread, base::Unretained(this))); 339 } 340 341 // Stop() will wait for any posted messages to be processed first. 342 audio_thread_.Stop(); 343} 344 345void AudioManagerBase::ShutdownOnAudioThread() { 346 DCHECK(message_loop_->BelongsToCurrentThread()); 347 348 AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); 349 for (; it != output_dispatchers_.end(); ++it) { 350 scoped_refptr<AudioOutputDispatcher>& dispatcher = (*it)->dispatcher; 351 dispatcher->Shutdown(); 352 353 // All AudioOutputProxies must have been freed before Shutdown is called. 354 // If they still exist, things will go bad. They have direct pointers to 355 // both physical audio stream objects that belong to the dispatcher as 356 // well as the message loop of the audio thread that will soon go away. 357 // So, better crash now than later. 358 DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive"; 359 dispatcher = NULL; 360 } 361 362 output_dispatchers_.clear(); 363} 364 365void AudioManagerBase::AddOutputDeviceChangeListener( 366 AudioDeviceListener* listener) { 367 DCHECK(message_loop_->BelongsToCurrentThread()); 368 output_listeners_.AddObserver(listener); 369} 370 371void AudioManagerBase::RemoveOutputDeviceChangeListener( 372 AudioDeviceListener* listener) { 373 DCHECK(message_loop_->BelongsToCurrentThread()); 374 output_listeners_.RemoveObserver(listener); 375} 376 377void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() { 378 DCHECK(message_loop_->BelongsToCurrentThread()); 379 DVLOG(1) << "Firing OnDeviceChange() notifications."; 380 FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange()); 381} 382 383AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() { 384 return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(), 385 AudioParameters()); 386} 387 388AudioParameters AudioManagerBase::GetOutputStreamParameters( 389 const std::string& device_id) { 390 return GetPreferredOutputStreamParameters(device_id, 391 AudioParameters()); 392} 393 394AudioParameters AudioManagerBase::GetInputStreamParameters( 395 const std::string& device_id) { 396 NOTREACHED(); 397 return AudioParameters(); 398} 399 400std::string AudioManagerBase::GetAssociatedOutputDeviceID( 401 const std::string& input_device_id) { 402 NOTIMPLEMENTED(); 403 return ""; 404} 405 406std::string AudioManagerBase::GetDefaultOutputDeviceID() { 407 return ""; 408} 409 410int AudioManagerBase::GetUserBufferSize() { 411 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 412 int buffer_size = 0; 413 std::string buffer_size_str(cmd_line->GetSwitchValueASCII( 414 switches::kAudioBufferSize)); 415 if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0) 416 return buffer_size; 417 418 return 0; 419} 420 421} // namespace media 422