Chromium Code Reviews| 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_output_controller.h" | 5 #include "media/audio/audio_output_controller.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
| 9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 10 #include "base/synchronization/waitable_event.h" | 10 #include "base/synchronization/waitable_event.h" |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 22 | 22 |
| 23 // Polling-related constants. | 23 // Polling-related constants. |
| 24 const int AudioOutputController::kPollNumAttempts = 3; | 24 const int AudioOutputController::kPollNumAttempts = 3; |
| 25 const int AudioOutputController::kPollPauseInMilliseconds = 3; | 25 const int AudioOutputController::kPollPauseInMilliseconds = 3; |
| 26 | 26 |
| 27 AudioOutputController::AudioOutputController(AudioManager* audio_manager, | 27 AudioOutputController::AudioOutputController(AudioManager* audio_manager, |
| 28 EventHandler* handler, | 28 EventHandler* handler, |
| 29 const AudioParameters& params, | 29 const AudioParameters& params, |
| 30 SyncReader* sync_reader) | 30 SyncReader* sync_reader) |
| 31 : audio_manager_(audio_manager), | 31 : audio_manager_(audio_manager), |
| 32 params_(params), | |
| 32 handler_(handler), | 33 handler_(handler), |
| 33 stream_(NULL), | 34 stream_(NULL), |
| 35 diverting_to_stream_(NULL), | |
| 34 volume_(1.0), | 36 volume_(1.0), |
| 35 state_(kEmpty), | 37 state_(kEmpty), |
| 36 sync_reader_(sync_reader), | 38 sync_reader_(sync_reader), |
| 37 message_loop_(audio_manager->GetMessageLoop()), | 39 message_loop_(audio_manager->GetMessageLoop()), |
| 38 number_polling_attempts_left_(0), | 40 number_polling_attempts_left_(0), |
| 39 params_(params), | |
| 40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { | 41 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { |
| 42 // This thread-checker is only used to ensure calls to StartDiverting() and | |
| 43 // StopDiverting() are made by the same single outside thread. | |
| 44 divert_thread_checker_.DetachFromThread(); | |
| 41 } | 45 } |
| 42 | 46 |
| 43 AudioOutputController::~AudioOutputController() { | 47 AudioOutputController::~AudioOutputController() { |
| 44 DCHECK_EQ(kClosed, state_); | 48 DCHECK_EQ(kClosed, state_); |
| 45 | 49 |
| 46 if (message_loop_->BelongsToCurrentThread()) { | 50 if (message_loop_->BelongsToCurrentThread()) { |
| 47 DoStopCloseAndClearStream(NULL); | 51 DoStopCloseAndClearStream(NULL); |
| 48 } else { | 52 } else { |
| 49 // http://crbug.com/120973 | 53 // http://crbug.com/120973 |
| 50 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 54 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 63 AudioManager* audio_manager, | 67 AudioManager* audio_manager, |
| 64 EventHandler* event_handler, | 68 EventHandler* event_handler, |
| 65 const AudioParameters& params, | 69 const AudioParameters& params, |
| 66 SyncReader* sync_reader) { | 70 SyncReader* sync_reader) { |
| 67 DCHECK(audio_manager); | 71 DCHECK(audio_manager); |
| 68 DCHECK(sync_reader); | 72 DCHECK(sync_reader); |
| 69 | 73 |
| 70 if (!params.IsValid() || !audio_manager) | 74 if (!params.IsValid() || !audio_manager) |
| 71 return NULL; | 75 return NULL; |
| 72 | 76 |
| 73 // Starts the audio controller thread. | |
| 74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( | 77 scoped_refptr<AudioOutputController> controller(new AudioOutputController( |
| 75 audio_manager, event_handler, params, sync_reader)); | 78 audio_manager, event_handler, params, sync_reader)); |
| 76 | |
| 77 controller->message_loop_->PostTask(FROM_HERE, base::Bind( | 79 controller->message_loop_->PostTask(FROM_HERE, base::Bind( |
| 78 &AudioOutputController::DoCreate, controller)); | 80 &AudioOutputController::DoCreate, controller)); |
| 79 | |
| 80 return controller; | 81 return controller; |
| 81 } | 82 } |
| 82 | 83 |
| 83 void AudioOutputController::Play() { | 84 void AudioOutputController::Play() { |
| 84 DCHECK(message_loop_); | 85 DCHECK(message_loop_); |
| 85 message_loop_->PostTask(FROM_HERE, base::Bind( | 86 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 86 &AudioOutputController::DoPlay, this)); | 87 &AudioOutputController::DoPlay, this)); |
| 87 } | 88 } |
| 88 | 89 |
| 89 void AudioOutputController::Pause() { | 90 void AudioOutputController::Pause() { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 113 | 114 |
| 114 void AudioOutputController::DoCreate() { | 115 void AudioOutputController::DoCreate() { |
| 115 DCHECK(message_loop_->BelongsToCurrentThread()); | 116 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 116 | 117 |
| 117 // Close() can be called before DoCreate() is executed. | 118 // Close() can be called before DoCreate() is executed. |
| 118 if (state_ == kClosed) | 119 if (state_ == kClosed) |
| 119 return; | 120 return; |
| 120 DCHECK(state_ == kEmpty || state_ == kRecreating) << state_; | 121 DCHECK(state_ == kEmpty || state_ == kRecreating) << state_; |
| 121 | 122 |
| 122 DoStopCloseAndClearStream(NULL); | 123 DoStopCloseAndClearStream(NULL); |
| 123 stream_ = audio_manager_->MakeAudioOutputStreamProxy(params_); | 124 stream_ = diverting_to_stream_ ? diverting_to_stream_ : |
| 125 audio_manager_->MakeAudioOutputStreamProxy(params_); | |
| 124 if (!stream_) { | 126 if (!stream_) { |
| 125 state_ = kError; | 127 state_ = kError; |
| 126 | 128 |
| 127 // TODO(hclam): Define error types. | 129 // TODO(hclam): Define error types. |
| 128 handler_->OnError(this, 0); | 130 handler_->OnError(this, 0); |
| 129 return; | 131 return; |
| 130 } | 132 } |
| 131 | 133 |
| 132 if (!stream_->Open()) { | 134 if (!stream_->Open()) { |
| 133 state_ = kError; | 135 state_ = kError; |
| 134 DoStopCloseAndClearStream(NULL); | 136 DoStopCloseAndClearStream(NULL); |
| 135 | 137 |
| 136 // TODO(hclam): Define error types. | 138 // TODO(hclam): Define error types. |
| 137 handler_->OnError(this, 0); | 139 handler_->OnError(this, 0); |
| 138 return; | 140 return; |
| 139 } | 141 } |
| 140 | 142 |
| 141 // Everything started okay, so register for state change callbacks if we have | 143 // Everything started okay, so re-register for state change callbacks if |
| 142 // not already done so. | 144 // stream_ was created via AudioManager. Note: The call to |
| 143 if (state_ != kRecreating) | 145 // DoStopCloseAndClearStream() above called |
| 146 // RemoveOutputDeviceChangeListener(). | |
| 147 if (stream_ != diverting_to_stream_) | |
| 144 audio_manager_->AddOutputDeviceChangeListener(this); | 148 audio_manager_->AddOutputDeviceChangeListener(this); |
| 145 | 149 |
| 146 // We have successfully opened the stream. Set the initial volume. | 150 // We have successfully opened the stream. Set the initial volume. |
| 147 stream_->SetVolume(volume_); | 151 stream_->SetVolume(volume_); |
| 148 | 152 |
| 149 // Finally set the state to kCreated. | 153 // Finally set the state to kCreated. |
| 150 State original_state = state_; | 154 State original_state = state_; |
| 151 state_ = kCreated; | 155 state_ = kCreated; |
| 152 | 156 |
| 153 // And then report we have been created if we haven't done so already. | 157 // And then report we have been created if we haven't done so already. |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 341 // Handle error on the audio controller thread. | 345 // Handle error on the audio controller thread. |
| 342 message_loop_->PostTask(FROM_HERE, base::Bind( | 346 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 343 &AudioOutputController::DoReportError, this, code)); | 347 &AudioOutputController::DoReportError, this, code)); |
| 344 } | 348 } |
| 345 | 349 |
| 346 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { | 350 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { |
| 347 DCHECK(message_loop_->BelongsToCurrentThread()); | 351 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 348 | 352 |
| 349 // Allow calling unconditionally and bail if we don't have a stream_ to close. | 353 // Allow calling unconditionally and bail if we don't have a stream_ to close. |
| 350 if (stream_) { | 354 if (stream_) { |
| 355 // De-register from state change callbacks if stream_ was created via | |
| 356 // AudioManager. | |
| 357 if (stream_ != diverting_to_stream_) | |
| 358 audio_manager_->RemoveOutputDeviceChangeListener(this); | |
| 359 | |
| 351 stream_->Stop(); | 360 stream_->Stop(); |
| 352 stream_->Close(); | 361 stream_->Close(); |
| 362 if (stream_ == diverting_to_stream_) | |
| 363 diverting_to_stream_ = NULL; | |
| 353 stream_ = NULL; | 364 stream_ = NULL; |
| 354 | 365 |
| 355 audio_manager_->RemoveOutputDeviceChangeListener(this); | |
| 356 audio_manager_ = NULL; | |
| 357 | |
| 358 weak_this_.InvalidateWeakPtrs(); | 366 weak_this_.InvalidateWeakPtrs(); |
| 359 } | 367 } |
| 360 | 368 |
| 361 // Should be last in the method, do not touch "this" from here on. | 369 // Should be last in the method, do not touch "this" from here on. |
| 362 if (done) | 370 if (done) |
| 363 done->Signal(); | 371 done->Signal(); |
| 364 } | 372 } |
| 365 | 373 |
| 366 void AudioOutputController::OnDeviceChange() { | 374 void AudioOutputController::OnDeviceChange() { |
| 367 DCHECK(message_loop_->BelongsToCurrentThread()); | 375 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 368 | 376 |
| 369 // We should always have a stream by this point. | 377 // Recreate the stream (DoCreate() will first shut down an existing stream). |
| 370 CHECK(stream_); | 378 // Exit if we ran into an error. |
| 371 | 379 const State original_state = state_; |
| 372 // Preserve the original state and shutdown the stream. | |
| 373 State original_state = state_; | |
| 374 stream_->Stop(); | |
| 375 stream_->Close(); | |
| 376 stream_ = NULL; | |
| 377 | |
| 378 // Recreate the stream, exit if we ran into an error. | |
| 379 state_ = kRecreating; | 380 state_ = kRecreating; |
| 380 DoCreate(); | 381 DoCreate(); |
| 381 if (!stream_ || state_ == kError) | 382 if (!stream_ || state_ == kError) |
| 382 return; | 383 return; |
| 383 | 384 |
| 384 // Get us back to the original state or an equivalent state. | 385 // Get us back to the original state or an equivalent state. |
| 385 switch (original_state) { | 386 switch (original_state) { |
| 386 case kStarting: | 387 case kStarting: |
| 387 case kPlaying: | 388 case kPlaying: |
| 388 DoPlay(); | 389 DoPlay(); |
| 389 return; | 390 return; |
| 390 case kCreated: | 391 case kCreated: |
| 391 case kPausedWhenStarting: | 392 case kPausedWhenStarting: |
| 392 case kPaused: | 393 case kPaused: |
| 393 // From the outside these three states are equivalent. | 394 // From the outside these three states are equivalent. |
| 394 return; | 395 return; |
| 395 default: | 396 default: |
| 396 NOTREACHED() << "Invalid original state."; | 397 NOTREACHED() << "Invalid original state."; |
| 397 } | 398 } |
| 398 } | 399 } |
| 399 | 400 |
| 401 void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) { | |
| 402 if (!message_loop_->BelongsToCurrentThread()) { | |
|
DaleCurtis
2013/01/02 21:56:44
This is still odd and inconsistent with the rest o
miu
2013/01/03 22:30:42
Done.
| |
| 403 DCHECK(divert_thread_checker_.CalledOnValidThread()); | |
| 404 message_loop_->PostTask( | |
| 405 FROM_HERE, | |
| 406 base::Bind(&AudioOutputController::StartDiverting, this, to_stream)); | |
| 407 return; | |
| 408 } | |
| 409 | |
| 410 DCHECK(!diverting_to_stream_); | |
| 411 diverting_to_stream_ = to_stream; | |
| 412 DCHECK_NE(kClosed, state_); | |
|
DaleCurtis
2013/01/02 21:56:44
Instead of a DHCECK_NE, shouldn't this be an if (s
miu
2013/01/03 22:30:42
Done.
| |
| 413 OnDeviceChange(); | |
| 414 } | |
| 415 | |
| 416 void AudioOutputController::StopDiverting() { | |
|
DaleCurtis
2013/01/02 21:56:44
Ditto.
miu
2013/01/03 22:30:42
Done.
| |
| 417 if (!message_loop_->BelongsToCurrentThread()) { | |
| 418 DCHECK(divert_thread_checker_.CalledOnValidThread()); | |
| 419 message_loop_->PostTask( | |
| 420 FROM_HERE, base::Bind(&AudioOutputController::StopDiverting, this)); | |
| 421 return; | |
| 422 } | |
| 423 | |
| 424 if (state_ != kClosed) { | |
| 425 OnDeviceChange(); | |
|
DaleCurtis
2013/01/02 21:56:44
This needs a comment since it's subtle that OnDevi
miu
2013/01/03 22:30:42
Done.
| |
| 426 DCHECK(!diverting_to_stream_); | |
| 427 } | |
| 428 } | |
| 429 | |
| 400 } // namespace media | 430 } // namespace media |
| OLD | NEW |