OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/audio_manager_base.h" | 5 #include "media/audio/audio_manager_base.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/message_loop/message_loop_proxy.h" | |
11 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
12 #include "build/build_config.h" | 11 #include "build/build_config.h" |
13 #include "media/audio/audio_output_dispatcher_impl.h" | 12 #include "media/audio/audio_output_dispatcher_impl.h" |
14 #include "media/audio/audio_output_proxy.h" | 13 #include "media/audio/audio_output_proxy.h" |
15 #include "media/audio/audio_output_resampler.h" | 14 #include "media/audio/audio_output_resampler.h" |
16 #include "media/audio/fake_audio_input_stream.h" | 15 #include "media/audio/fake_audio_input_stream.h" |
17 #include "media/audio/fake_audio_output_stream.h" | 16 #include "media/audio/fake_audio_output_stream.h" |
18 #include "media/base/media_switches.h" | 17 #include "media/base/media_switches.h" |
19 | 18 |
20 namespace media { | 19 namespace media { |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 audio_thread_.init_com_with_mta(true); | 91 audio_thread_.init_com_with_mta(true); |
93 #elif defined(OS_MACOSX) | 92 #elif defined(OS_MACOSX) |
94 // CoreAudio calls must occur on the main thread of the process, which in our | 93 // 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 | 94 // 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. | 95 // 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. | 96 // TODO(dalecurtis): We should require the message loop to be passed in. |
98 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 97 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
99 if (!cmd_line->HasSwitch(switches::kDisableMainThreadAudio) && | 98 if (!cmd_line->HasSwitch(switches::kDisableMainThreadAudio) && |
100 base::MessageLoopProxy::current().get() && | 99 base::MessageLoopProxy::current().get() && |
101 base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI)) { | 100 base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI)) { |
102 message_loop_ = base::MessageLoopProxy::current(); | 101 task_runner_ = base::MessageLoopProxy::current(); |
103 return; | 102 return; |
104 } | 103 } |
105 #endif | 104 #endif |
106 | 105 |
107 CHECK(audio_thread_.Start()); | 106 CHECK(audio_thread_.Start()); |
108 message_loop_ = audio_thread_.message_loop_proxy(); | 107 task_runner_ = audio_thread_.message_loop_proxy(); |
109 } | 108 } |
110 | 109 |
111 AudioManagerBase::~AudioManagerBase() { | 110 AudioManagerBase::~AudioManagerBase() { |
112 // The platform specific AudioManager implementation must have already | 111 // The platform specific AudioManager implementation must have already |
113 // stopped the audio thread. Otherwise, we may destroy audio streams before | 112 // stopped the audio thread. Otherwise, we may destroy audio streams before |
114 // stopping the thread, resulting an unexpected behavior. | 113 // stopping the thread, resulting an unexpected behavior. |
115 // This way we make sure activities of the audio streams are all stopped | 114 // This way we make sure activities of the audio streams are all stopped |
116 // before we destroy them. | 115 // before we destroy them. |
117 CHECK(!audio_thread_.IsRunning()); | 116 CHECK(!audio_thread_.IsRunning()); |
118 // All the output streams should have been deleted. | 117 // All the output streams should have been deleted. |
119 DCHECK_EQ(0, num_output_streams_); | 118 DCHECK_EQ(0, num_output_streams_); |
120 // All the input streams should have been deleted. | 119 // All the input streams should have been deleted. |
121 DCHECK_EQ(0, num_input_streams_); | 120 DCHECK_EQ(0, num_input_streams_); |
122 } | 121 } |
123 | 122 |
124 base::string16 AudioManagerBase::GetAudioInputDeviceModel() { | 123 base::string16 AudioManagerBase::GetAudioInputDeviceModel() { |
125 return base::string16(); | 124 return base::string16(); |
126 } | 125 } |
127 | 126 |
128 scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() { | 127 scoped_refptr<base::SingleThreadTaskRunner> AudioManagerBase::GetTaskRunner() { |
129 return message_loop_; | 128 return task_runner_; |
130 } | 129 } |
131 | 130 |
132 scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetWorkerLoop() { | 131 scoped_refptr<base::SingleThreadTaskRunner> |
| 132 AudioManagerBase::GetWorkerTaskRunner() { |
133 // Lazily start the worker thread. | 133 // Lazily start the worker thread. |
134 if (!audio_thread_.IsRunning()) | 134 if (!audio_thread_.IsRunning()) |
135 CHECK(audio_thread_.Start()); | 135 CHECK(audio_thread_.Start()); |
136 | 136 |
137 return audio_thread_.message_loop_proxy(); | 137 return audio_thread_.message_loop_proxy(); |
138 } | 138 } |
139 | 139 |
140 AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( | 140 AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( |
141 const AudioParameters& params, | 141 const AudioParameters& params, |
142 const std::string& device_id, | 142 const std::string& device_id, |
143 const std::string& input_device_id) { | 143 const std::string& input_device_id) { |
144 // TODO(miu): Fix ~50 call points across several unit test modules to call | 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: | 145 // this method on the audio thread, then uncomment the following: |
146 // DCHECK(message_loop_->BelongsToCurrentThread()); | 146 // DCHECK(task_runner_->BelongsToCurrentThread()); |
147 | 147 |
148 if (!params.IsValid()) { | 148 if (!params.IsValid()) { |
149 DLOG(ERROR) << "Audio parameters are invalid"; | 149 DLOG(ERROR) << "Audio parameters are invalid"; |
150 return NULL; | 150 return NULL; |
151 } | 151 } |
152 | 152 |
153 // Limit the number of audio streams opened. This is to prevent using | 153 // Limit the number of audio streams opened. This is to prevent using |
154 // excessive resources for a large number of audio streams. More | 154 // excessive resources for a large number of audio streams. More |
155 // importantly it prevents instability on certain systems. | 155 // importantly it prevents instability on certain systems. |
156 // See bug: http://crbug.com/30242. | 156 // See bug: http://crbug.com/30242. |
(...skipping 28 matching lines...) Expand all Loading... |
185 } | 185 } |
186 | 186 |
187 return stream; | 187 return stream; |
188 } | 188 } |
189 | 189 |
190 AudioInputStream* AudioManagerBase::MakeAudioInputStream( | 190 AudioInputStream* AudioManagerBase::MakeAudioInputStream( |
191 const AudioParameters& params, | 191 const AudioParameters& params, |
192 const std::string& device_id) { | 192 const std::string& device_id) { |
193 // TODO(miu): Fix ~20 call points across several unit test modules to call | 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: | 194 // this method on the audio thread, then uncomment the following: |
195 // DCHECK(message_loop_->BelongsToCurrentThread()); | 195 // DCHECK(task_runner_->BelongsToCurrentThread()); |
196 | 196 |
197 if (!params.IsValid() || (params.channels() > kMaxInputChannels) || | 197 if (!params.IsValid() || (params.channels() > kMaxInputChannels) || |
198 device_id.empty()) { | 198 device_id.empty()) { |
199 DLOG(ERROR) << "Audio parameters are invalid for device " << device_id; | 199 DLOG(ERROR) << "Audio parameters are invalid for device " << device_id; |
200 return NULL; | 200 return NULL; |
201 } | 201 } |
202 | 202 |
203 if (num_input_streams_ >= max_num_input_streams_) { | 203 if (num_input_streams_ >= max_num_input_streams_) { |
204 DLOG(ERROR) << "Number of opened input audio streams " | 204 DLOG(ERROR) << "Number of opened input audio streams " |
205 << num_input_streams_ | 205 << num_input_streams_ |
(...skipping 21 matching lines...) Expand all Loading... |
227 ++num_input_streams_; | 227 ++num_input_streams_; |
228 } | 228 } |
229 | 229 |
230 return stream; | 230 return stream; |
231 } | 231 } |
232 | 232 |
233 AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( | 233 AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( |
234 const AudioParameters& params, | 234 const AudioParameters& params, |
235 const std::string& device_id, | 235 const std::string& device_id, |
236 const std::string& input_device_id) { | 236 const std::string& input_device_id) { |
237 DCHECK(message_loop_->BelongsToCurrentThread()); | 237 DCHECK(task_runner_->BelongsToCurrentThread()); |
238 | 238 |
239 // If the caller supplied an empty device id to select the default device, | 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 | 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 | 241 // will find the correct device regardless of whether it was opened as |
242 // "default" or via the specific id. | 242 // "default" or via the specific id. |
243 // NOTE: Implementations that don't yet support opening non-default output | 243 // NOTE: Implementations that don't yet support opening non-default output |
244 // devices may return an empty string from GetDefaultOutputDeviceID(). | 244 // devices may return an empty string from GetDefaultOutputDeviceID(). |
245 std::string output_device_id = device_id.empty() ? | 245 std::string output_device_id = device_id.empty() ? |
246 GetDefaultOutputDeviceID() : device_id; | 246 GetDefaultOutputDeviceID() : device_id; |
247 | 247 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
325 void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) { | 325 void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) { |
326 DCHECK(stream); | 326 DCHECK(stream); |
327 // TODO(xians) : Have a clearer destruction path for the AudioInputStream. | 327 // TODO(xians) : Have a clearer destruction path for the AudioInputStream. |
328 --num_input_streams_; | 328 --num_input_streams_; |
329 delete stream; | 329 delete stream; |
330 } | 330 } |
331 | 331 |
332 void AudioManagerBase::Shutdown() { | 332 void AudioManagerBase::Shutdown() { |
333 // Only true when we're sharing the UI message loop with the browser. The UI | 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. | 334 // loop is no longer running at this time and browser destruction is imminent. |
335 if (message_loop_->BelongsToCurrentThread()) { | 335 if (task_runner_->BelongsToCurrentThread()) { |
336 ShutdownOnAudioThread(); | 336 ShutdownOnAudioThread(); |
337 } else { | 337 } else { |
338 message_loop_->PostTask(FROM_HERE, base::Bind( | 338 task_runner_->PostTask(FROM_HERE, base::Bind( |
339 &AudioManagerBase::ShutdownOnAudioThread, base::Unretained(this))); | 339 &AudioManagerBase::ShutdownOnAudioThread, base::Unretained(this))); |
340 } | 340 } |
341 | 341 |
342 // Stop() will wait for any posted messages to be processed first. | 342 // Stop() will wait for any posted messages to be processed first. |
343 audio_thread_.Stop(); | 343 audio_thread_.Stop(); |
344 } | 344 } |
345 | 345 |
346 void AudioManagerBase::ShutdownOnAudioThread() { | 346 void AudioManagerBase::ShutdownOnAudioThread() { |
347 DCHECK(message_loop_->BelongsToCurrentThread()); | 347 DCHECK(task_runner_->BelongsToCurrentThread()); |
348 | 348 |
349 AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); | 349 AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); |
350 for (; it != output_dispatchers_.end(); ++it) { | 350 for (; it != output_dispatchers_.end(); ++it) { |
351 scoped_refptr<AudioOutputDispatcher>& dispatcher = (*it)->dispatcher; | 351 scoped_refptr<AudioOutputDispatcher>& dispatcher = (*it)->dispatcher; |
352 dispatcher->Shutdown(); | 352 dispatcher->Shutdown(); |
353 | 353 |
354 // All AudioOutputProxies must have been freed before Shutdown is called. | 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 | 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 | 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. | 357 // well as the message loop of the audio thread that will soon go away. |
358 // So, better crash now than later. | 358 // So, better crash now than later. |
359 DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive"; | 359 DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive"; |
360 dispatcher = NULL; | 360 dispatcher = NULL; |
361 } | 361 } |
362 | 362 |
363 output_dispatchers_.clear(); | 363 output_dispatchers_.clear(); |
364 } | 364 } |
365 | 365 |
366 void AudioManagerBase::AddOutputDeviceChangeListener( | 366 void AudioManagerBase::AddOutputDeviceChangeListener( |
367 AudioDeviceListener* listener) { | 367 AudioDeviceListener* listener) { |
368 DCHECK(message_loop_->BelongsToCurrentThread()); | 368 DCHECK(task_runner_->BelongsToCurrentThread()); |
369 output_listeners_.AddObserver(listener); | 369 output_listeners_.AddObserver(listener); |
370 } | 370 } |
371 | 371 |
372 void AudioManagerBase::RemoveOutputDeviceChangeListener( | 372 void AudioManagerBase::RemoveOutputDeviceChangeListener( |
373 AudioDeviceListener* listener) { | 373 AudioDeviceListener* listener) { |
374 DCHECK(message_loop_->BelongsToCurrentThread()); | 374 DCHECK(task_runner_->BelongsToCurrentThread()); |
375 output_listeners_.RemoveObserver(listener); | 375 output_listeners_.RemoveObserver(listener); |
376 } | 376 } |
377 | 377 |
378 void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() { | 378 void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() { |
379 DCHECK(message_loop_->BelongsToCurrentThread()); | 379 DCHECK(task_runner_->BelongsToCurrentThread()); |
380 DVLOG(1) << "Firing OnDeviceChange() notifications."; | 380 DVLOG(1) << "Firing OnDeviceChange() notifications."; |
381 FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange()); | 381 FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange()); |
382 } | 382 } |
383 | 383 |
384 AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() { | 384 AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() { |
385 return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(), | 385 return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(), |
386 AudioParameters()); | 386 AudioParameters()); |
387 } | 387 } |
388 | 388 |
389 AudioParameters AudioManagerBase::GetOutputStreamParameters( | 389 AudioParameters AudioManagerBase::GetOutputStreamParameters( |
(...skipping 28 matching lines...) Expand all Loading... |
418 | 418 |
419 return 0; | 419 return 0; |
420 } | 420 } |
421 | 421 |
422 scoped_ptr<AudioLog> AudioManagerBase::CreateAudioLog( | 422 scoped_ptr<AudioLog> AudioManagerBase::CreateAudioLog( |
423 AudioLogFactory::AudioComponent component) { | 423 AudioLogFactory::AudioComponent component) { |
424 return audio_log_factory_->CreateAudioLog(component); | 424 return audio_log_factory_->CreateAudioLog(component); |
425 } | 425 } |
426 | 426 |
427 void AudioManagerBase::FixWedgedAudio() { | 427 void AudioManagerBase::FixWedgedAudio() { |
428 DCHECK(message_loop_->BelongsToCurrentThread()); | 428 DCHECK(task_runner_->BelongsToCurrentThread()); |
429 #if defined(OS_MACOSX) | 429 #if defined(OS_MACOSX) |
430 // Through trial and error, we've found that one way to restore audio after a | 430 // Through trial and error, we've found that one way to restore audio after a |
431 // hang is to close all outstanding audio streams. Once all streams have been | 431 // hang is to close all outstanding audio streams. Once all streams have been |
432 // closed, new streams appear to work correctly. | 432 // closed, new streams appear to work correctly. |
433 // | 433 // |
434 // In Chrome terms, this means we need to ask all AudioOutputDispatchers to | 434 // In Chrome terms, this means we need to ask all AudioOutputDispatchers to |
435 // close all Open()'d streams. Once all streams across all dispatchers have | 435 // close all Open()'d streams. Once all streams across all dispatchers have |
436 // been closed, we ask for all previously Start()'d streams to be recreated | 436 // been closed, we ask for all previously Start()'d streams to be recreated |
437 // using the same AudioSourceCallback they had before. | 437 // using the same AudioSourceCallback they had before. |
438 // | 438 // |
439 // Since this operation takes place on the audio thread we can be sure that no | 439 // Since this operation takes place on the audio thread we can be sure that no |
440 // other state-changing stream operations will take place while the fix is in | 440 // other state-changing stream operations will take place while the fix is in |
441 // progress. | 441 // progress. |
442 // | 442 // |
443 // See http://crbug.com/160920 for additional details. | 443 // See http://crbug.com/160920 for additional details. |
444 for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); | 444 for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); |
445 it != output_dispatchers_.end(); ++it) { | 445 it != output_dispatchers_.end(); ++it) { |
446 (*it)->dispatcher->CloseStreamsForWedgeFix(); | 446 (*it)->dispatcher->CloseStreamsForWedgeFix(); |
447 } | 447 } |
448 for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); | 448 for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin(); |
449 it != output_dispatchers_.end(); ++it) { | 449 it != output_dispatchers_.end(); ++it) { |
450 (*it)->dispatcher->RestartStreamsForWedgeFix(); | 450 (*it)->dispatcher->RestartStreamsForWedgeFix(); |
451 } | 451 } |
452 #endif | 452 #endif |
453 } | 453 } |
454 | 454 |
455 } // namespace media | 455 } // namespace media |
OLD | NEW |