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