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), |
| 34 volume_(1.0), | 35 volume_(1.0), |
| 35 state_(kEmpty), | 36 state_(kEmpty), |
| 36 sync_reader_(sync_reader), | 37 sync_reader_(sync_reader), |
| 37 message_loop_(audio_manager->GetMessageLoop()), | 38 message_loop_(audio_manager->GetMessageLoop()), |
| 38 number_polling_attempts_left_(0), | 39 number_polling_attempts_left_(0), |
| 39 params_(params), | |
| 40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { | 40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { |
| 41 } | 41 } |
| 42 | 42 |
| 43 AudioOutputController::~AudioOutputController() { | 43 AudioOutputController::~AudioOutputController() { |
| 44 DCHECK_EQ(kClosed, state_); | 44 DCHECK_EQ(kClosed, state_); |
| 45 | 45 |
| 46 if (message_loop_->BelongsToCurrentThread()) { | 46 if (message_loop_->BelongsToCurrentThread()) { |
| 47 DoStopCloseAndClearStream(NULL); | 47 DoStopCloseAndClearStream(NULL); |
| 48 } else { | 48 } else { |
| 49 // http://crbug.com/120973 | 49 // http://crbug.com/120973 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 63 AudioManager* audio_manager, | 63 AudioManager* audio_manager, |
| 64 EventHandler* event_handler, | 64 EventHandler* event_handler, |
| 65 const AudioParameters& params, | 65 const AudioParameters& params, |
| 66 SyncReader* sync_reader) { | 66 SyncReader* sync_reader) { |
| 67 DCHECK(audio_manager); | 67 DCHECK(audio_manager); |
| 68 DCHECK(sync_reader); | 68 DCHECK(sync_reader); |
| 69 | 69 |
| 70 if (!params.IsValid() || !audio_manager) | 70 if (!params.IsValid() || !audio_manager) |
| 71 return NULL; | 71 return NULL; |
| 72 | 72 |
| 73 // Starts the audio controller thread. | |
| 74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( | 73 scoped_refptr<AudioOutputController> controller(new AudioOutputController( |
| 75 audio_manager, event_handler, params, sync_reader)); | 74 audio_manager, event_handler, params, sync_reader)); |
| 76 | |
| 77 controller->message_loop_->PostTask(FROM_HERE, base::Bind( | 75 controller->message_loop_->PostTask(FROM_HERE, base::Bind( |
| 78 &AudioOutputController::DoCreate, controller)); | 76 &AudioOutputController::DoCreate, controller)); |
| 79 | |
| 80 return controller; | 77 return controller; |
| 81 } | 78 } |
| 82 | 79 |
| 83 void AudioOutputController::Play() { | 80 void AudioOutputController::Play() { |
| 84 DCHECK(message_loop_); | 81 DCHECK(message_loop_); |
| 85 message_loop_->PostTask(FROM_HERE, base::Bind( | 82 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 86 &AudioOutputController::DoPlay, this)); | 83 &AudioOutputController::DoPlay, this)); |
| 87 } | 84 } |
| 88 | 85 |
| 89 void AudioOutputController::Pause() { | 86 void AudioOutputController::Pause() { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 131 | 128 |
| 132 if (!stream_->Open()) { | 129 if (!stream_->Open()) { |
| 133 state_ = kError; | 130 state_ = kError; |
| 134 DoStopCloseAndClearStream(NULL); | 131 DoStopCloseAndClearStream(NULL); |
| 135 | 132 |
| 136 // TODO(hclam): Define error types. | 133 // TODO(hclam): Define error types. |
| 137 handler_->OnError(this, 0); | 134 handler_->OnError(this, 0); |
| 138 return; | 135 return; |
| 139 } | 136 } |
| 140 | 137 |
| 141 // Everything started okay, so register for state change callbacks if we have | 138 // Everything started okay, so re-register for state change callbacks (the |
| 142 // not already done so. | 139 // call to DoStopCloseAndClearStream() above de-registered this instance). |
| 143 if (state_ != kRecreating) | 140 audio_manager_->AddOutputDeviceChangeListener(this); |
|
DaleCurtis
2012/12/05 23:35:14
I don't see where you're removing the device chang
miu
2012/12/11 02:30:45
The call to DoStopCloseAndClearStream() above call
| |
| 144 audio_manager_->AddOutputDeviceChangeListener(this); | |
| 145 | 141 |
| 146 // We have successfully opened the stream. Set the initial volume. | 142 // We have successfully opened the stream. Set the initial volume. |
| 147 stream_->SetVolume(volume_); | 143 stream_->SetVolume(volume_); |
| 148 | 144 |
| 149 // Finally set the state to kCreated. | 145 // Finally set the state to kCreated. |
| 150 State original_state = state_; | 146 State original_state = state_; |
| 151 state_ = kCreated; | 147 state_ = kCreated; |
| 152 | 148 |
| 153 // And then report we have been created if we haven't done so already. | 149 // And then report we have been created if we haven't done so already. |
| 154 if (original_state != kRecreating) | 150 if (original_state != kRecreating) |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 208 weak_this_.GetWeakPtr()), | 204 weak_this_.GetWeakPtr()), |
| 209 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | 205 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); |
| 210 } | 206 } |
| 211 } | 207 } |
| 212 | 208 |
| 213 void AudioOutputController::StartStream() { | 209 void AudioOutputController::StartStream() { |
| 214 DCHECK(message_loop_->BelongsToCurrentThread()); | 210 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 215 state_ = kPlaying; | 211 state_ = kPlaying; |
| 216 | 212 |
| 217 // We start the AudioOutputStream lazily. | 213 // We start the AudioOutputStream lazily. |
| 218 stream_->Start(this); | 214 if (stream_) |
|
DaleCurtis
2012/12/05 23:35:14
stream_ should always be valid here. why not check
miu
2012/12/11 02:30:45
I had changed the semantics so that stream_ is NUL
| |
| 215 stream_->Start(this); | |
| 219 | 216 |
| 220 // Tell the event handler that we are now playing. | 217 // Tell the event handler that we are now playing. |
| 221 handler_->OnPlaying(this); | 218 handler_->OnPlaying(this); |
| 222 } | 219 } |
| 223 | 220 |
| 224 void AudioOutputController::DoPause() { | 221 void AudioOutputController::DoPause() { |
| 225 DCHECK(message_loop_->BelongsToCurrentThread()); | 222 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 226 | 223 |
| 227 if (stream_) { | 224 if (stream_) { |
| 228 // Then we stop the audio device. This is not the perfect solution | 225 // Then we stop the audio device. This is not the perfect solution |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 AudioBuffersState buffers_state) { | 295 AudioBuffersState buffers_state) { |
| 299 return OnMoreIOData(NULL, dest, buffers_state); | 296 return OnMoreIOData(NULL, dest, buffers_state); |
| 300 } | 297 } |
| 301 | 298 |
| 302 int AudioOutputController::OnMoreIOData(AudioBus* source, | 299 int AudioOutputController::OnMoreIOData(AudioBus* source, |
| 303 AudioBus* dest, | 300 AudioBus* dest, |
| 304 AudioBuffersState buffers_state) { | 301 AudioBuffersState buffers_state) { |
| 305 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); | 302 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); |
| 306 | 303 |
| 307 { | 304 { |
| 308 // Check state and do nothing if we are not playing. | 305 // Check state. When not playing, fill the destination with zeros. |
| 309 // We are on the hardware audio thread, so lock is needed. | 306 // We are on the hardware audio thread, so lock is needed. |
| 310 base::AutoLock auto_lock(lock_); | 307 base::AutoLock auto_lock(lock_); |
| 311 if (state_ != kPlaying) { | 308 if (state_ != kPlaying) { |
| 312 return 0; | 309 dest->Zero(); |
| 310 return dest->frames(); | |
| 313 } | 311 } |
| 314 } | 312 } |
| 315 | 313 |
| 316 int frames = sync_reader_->Read(source, dest); | 314 int frames = sync_reader_->Read(source, dest); |
| 317 sync_reader_->UpdatePendingBytes( | 315 sync_reader_->UpdatePendingBytes( |
| 318 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); | 316 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); |
| 319 return frames; | 317 return frames; |
| 320 } | 318 } |
| 321 | 319 |
| 322 void AudioOutputController::WaitTillDataReady() { | 320 void AudioOutputController::WaitTillDataReady() { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 346 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { | 344 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { |
| 347 DCHECK(message_loop_->BelongsToCurrentThread()); | 345 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 348 | 346 |
| 349 // Allow calling unconditionally and bail if we don't have a stream_ to close. | 347 // Allow calling unconditionally and bail if we don't have a stream_ to close. |
| 350 if (stream_) { | 348 if (stream_) { |
| 351 stream_->Stop(); | 349 stream_->Stop(); |
| 352 stream_->Close(); | 350 stream_->Close(); |
| 353 stream_ = NULL; | 351 stream_ = NULL; |
| 354 | 352 |
| 355 audio_manager_->RemoveOutputDeviceChangeListener(this); | 353 audio_manager_->RemoveOutputDeviceChangeListener(this); |
| 356 audio_manager_ = NULL; | |
| 357 | 354 |
| 358 weak_this_.InvalidateWeakPtrs(); | 355 weak_this_.InvalidateWeakPtrs(); |
| 359 } | 356 } |
| 360 | 357 |
| 361 // Should be last in the method, do not touch "this" from here on. | 358 // Should be last in the method, do not touch "this" from here on. |
| 362 if (done) | 359 if (done) |
| 363 done->Signal(); | 360 done->Signal(); |
| 364 } | 361 } |
| 365 | 362 |
| 366 void AudioOutputController::OnDeviceChange() { | 363 void AudioOutputController::OnDeviceChange() { |
| 367 DCHECK(message_loop_->BelongsToCurrentThread()); | 364 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 368 | 365 |
| 369 // We should always have a stream by this point. | 366 // Preserve the original state. |
|
DaleCurtis
2012/12/05 23:35:14
Is this not true anymore?
miu
2012/12/11 02:30:45
Done. (Reverted back to original behavior.)
| |
| 370 CHECK(stream_); | 367 State original_state = state_; |
| 371 | 368 |
| 372 // Preserve the original state and shutdown the stream. | 369 // Recreate the stream (DoCreate() will first shut down an existing stream). |
| 373 State original_state = state_; | 370 // Exit if we ran into an error. |
| 374 stream_->Stop(); | |
| 375 stream_->Close(); | |
| 376 stream_ = NULL; | |
| 377 | |
| 378 // Recreate the stream, exit if we ran into an error. | |
| 379 state_ = kRecreating; | 371 state_ = kRecreating; |
| 380 DoCreate(); | 372 DoCreate(); |
| 381 if (!stream_ || state_ == kError) | 373 if (!stream_ || state_ == kError) |
| 382 return; | 374 return; |
| 383 | 375 |
| 384 // Get us back to the original state or an equivalent state. | 376 // Get us back to the original state or an equivalent state. |
| 385 switch (original_state) { | 377 switch (original_state) { |
| 386 case kStarting: | 378 case kStarting: |
| 387 case kPlaying: | 379 case kPlaying: |
| 380 DoSetVolume(volume_); | |
| 388 DoPlay(); | 381 DoPlay(); |
| 389 return; | 382 return; |
| 390 case kCreated: | 383 case kCreated: |
| 391 case kPausedWhenStarting: | 384 case kPausedWhenStarting: |
| 392 case kPaused: | 385 case kPaused: |
| 393 // From the outside these three states are equivalent. | 386 // From the outside these three states are equivalent. |
| 394 return; | 387 return; |
| 395 default: | 388 default: |
| 396 NOTREACHED() << "Invalid original state."; | 389 NOTREACHED() << "Invalid original state."; |
| 397 } | 390 } |
| 398 } | 391 } |
| 399 | 392 |
| 393 AudioOutputStream::AudioSourceCallback* AudioOutputController::Divert() { | |
| 394 // Assumption: AudioRendererHost is calling this method on the IO | |
| 395 // BrowserThread. | |
| 396 DCHECK(!message_loop_->BelongsToCurrentThread()); | |
|
DaleCurtis
2012/12/05 23:35:14
Is this helpful? It doesn't really matter what thr
miu
2012/12/11 02:30:45
Done.
| |
| 397 | |
| 398 // Block until stream closure is complete because, otherwise, it's possible | |
| 399 // for two entities to be pulling audio data at the same time. In addition, | |
| 400 // a side effect of closing the stream is that this controller will be | |
| 401 // de-registered from device change events (from AudioManager) during the | |
| 402 // "divert." | |
| 403 base::WaitableEvent blocker(true, false); | |
| 404 message_loop_->PostTask( | |
| 405 FROM_HERE, | |
| 406 base::Bind(&AudioOutputController::DoStopCloseAndClearStream, this, | |
| 407 &blocker)); | |
| 408 blocker.Wait(); | |
|
DaleCurtis
2012/12/05 23:35:14
Hmmm, seems risky. You're blocking the IO thread w
miu
2012/12/11 02:30:45
I changed my approach, and came up with something
| |
| 409 | |
| 410 return this; | |
| 411 } | |
| 412 | |
| 413 void AudioOutputController::Revert( | |
| 414 AudioOutputStream::AudioSourceCallback* asc) { | |
| 415 // Assumption: AudioRendererHost is calling this method on the IO | |
| 416 // BrowserThread. | |
| 417 DCHECK(!message_loop_->BelongsToCurrentThread()); | |
| 418 | |
| 419 DCHECK_EQ(this, asc); | |
|
DaleCurtis
2012/12/05 23:35:14
Again seems odd.
miu
2012/12/11 02:30:45
Done.
| |
| 420 | |
| 421 // The following re-creates the normal audio output stream and places it in | |
| 422 // the correct playback state. It also re-registers this controller for | |
| 423 // device change events from AudioManager. | |
| 424 message_loop_->PostTask( | |
| 425 FROM_HERE, base::Bind(&AudioOutputController::OnDeviceChange, this)); | |
| 426 } | |
| 427 | |
| 400 } // namespace media | 428 } // namespace media |
| OLD | NEW |