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 stream_glue_(new Glue(this, sync_reader)), | |
| 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)) { |
| 41 } | 42 } |
| 42 | 43 |
| 43 AudioOutputController::~AudioOutputController() { | 44 AudioOutputController::~AudioOutputController() { |
| 44 DCHECK_EQ(kClosed, state_); | 45 DCHECK_EQ(kClosed, state_); |
| 45 | 46 |
| 46 if (message_loop_->BelongsToCurrentThread()) { | 47 if (message_loop_->BelongsToCurrentThread()) { |
| 47 DoStopCloseAndClearStream(NULL); | 48 DoStopCloseAndClearStream(NULL); |
| 48 } else { | 49 } else { |
| 49 // http://crbug.com/120973 | 50 // http://crbug.com/120973 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 63 AudioManager* audio_manager, | 64 AudioManager* audio_manager, |
| 64 EventHandler* event_handler, | 65 EventHandler* event_handler, |
| 65 const AudioParameters& params, | 66 const AudioParameters& params, |
| 66 SyncReader* sync_reader) { | 67 SyncReader* sync_reader) { |
| 67 DCHECK(audio_manager); | 68 DCHECK(audio_manager); |
| 68 DCHECK(sync_reader); | 69 DCHECK(sync_reader); |
| 69 | 70 |
| 70 if (!params.IsValid() || !audio_manager) | 71 if (!params.IsValid() || !audio_manager) |
| 71 return NULL; | 72 return NULL; |
| 72 | 73 |
| 73 // Starts the audio controller thread. | |
| 74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( | 74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( |
| 75 audio_manager, event_handler, params, sync_reader)); | 75 audio_manager, event_handler, params, sync_reader)); |
| 76 | |
| 77 controller->message_loop_->PostTask(FROM_HERE, base::Bind( | 76 controller->message_loop_->PostTask(FROM_HERE, base::Bind( |
| 78 &AudioOutputController::DoCreate, controller)); | 77 &AudioOutputController::DoCreate, controller)); |
| 79 | |
| 80 return controller; | 78 return controller; |
| 81 } | 79 } |
| 82 | 80 |
| 83 void AudioOutputController::Play() { | 81 void AudioOutputController::Play() { |
| 84 DCHECK(message_loop_); | 82 DCHECK(message_loop_); |
| 85 message_loop_->PostTask(FROM_HERE, base::Bind( | 83 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 86 &AudioOutputController::DoPlay, this)); | 84 &AudioOutputController::DoPlay, this)); |
| 87 } | 85 } |
| 88 | 86 |
| 89 void AudioOutputController::Pause() { | 87 void AudioOutputController::Pause() { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 131 | 129 |
| 132 if (!stream_->Open()) { | 130 if (!stream_->Open()) { |
| 133 state_ = kError; | 131 state_ = kError; |
| 134 DoStopCloseAndClearStream(NULL); | 132 DoStopCloseAndClearStream(NULL); |
| 135 | 133 |
| 136 // TODO(hclam): Define error types. | 134 // TODO(hclam): Define error types. |
| 137 handler_->OnError(this, 0); | 135 handler_->OnError(this, 0); |
| 138 return; | 136 return; |
| 139 } | 137 } |
| 140 | 138 |
| 141 // Everything started okay, so register for state change callbacks if we have | 139 // Everything started okay, so re-register for state change callbacks. Note: |
| 142 // not already done so. | 140 // The call to DoStopCloseAndClearStream() above called |
| 143 if (state_ != kRecreating) | 141 // RemoveOutputDeviceChangeListener(). |
| 144 audio_manager_->AddOutputDeviceChangeListener(this); | 142 audio_manager_->AddOutputDeviceChangeListener(this); |
| 145 | 143 |
| 146 // We have successfully opened the stream. Set the initial volume. | 144 // We have successfully opened the stream. Set the initial volume. |
| 147 stream_->SetVolume(volume_); | 145 stream_->SetVolume(volume_); |
| 148 | 146 |
| 149 // Finally set the state to kCreated. | 147 // Finally set the state to kCreated. |
| 150 State original_state = state_; | 148 State original_state = state_; |
| 151 state_ = kCreated; | 149 state_ = kCreated; |
| 152 | 150 |
| 153 // And then report we have been created if we haven't done so already. | 151 // And then report we have been created if we haven't done so already. |
| 154 if (original_state != kRecreating) | 152 if (original_state != kRecreating) |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 208 weak_this_.GetWeakPtr()), | 206 weak_this_.GetWeakPtr()), |
| 209 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | 207 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); |
| 210 } | 208 } |
| 211 } | 209 } |
| 212 | 210 |
| 213 void AudioOutputController::StartStream() { | 211 void AudioOutputController::StartStream() { |
| 214 DCHECK(message_loop_->BelongsToCurrentThread()); | 212 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 215 state_ = kPlaying; | 213 state_ = kPlaying; |
| 216 | 214 |
| 217 // We start the AudioOutputStream lazily. | 215 // We start the AudioOutputStream lazily. |
| 218 stream_->Start(this); | 216 stream_->Start(stream_glue_); |
| 219 | 217 |
| 220 // Tell the event handler that we are now playing. | 218 // Tell the event handler that we are now playing. |
| 221 handler_->OnPlaying(this); | 219 handler_->OnPlaying(this); |
| 222 } | 220 } |
| 223 | 221 |
| 224 void AudioOutputController::DoPause() { | 222 void AudioOutputController::DoPause() { |
| 225 DCHECK(message_loop_->BelongsToCurrentThread()); | 223 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 226 | 224 |
| 227 if (stream_) { | 225 if (stream_) { |
| 228 // Then we stop the audio device. This is not the perfect solution | 226 // Then we stop the audio device. This is not the perfect solution |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 255 void AudioOutputController::DoFlush() { | 253 void AudioOutputController::DoFlush() { |
| 256 DCHECK(message_loop_->BelongsToCurrentThread()); | 254 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 257 | 255 |
| 258 // TODO(hclam): Actually flush the audio device. | 256 // TODO(hclam): Actually flush the audio device. |
| 259 } | 257 } |
| 260 | 258 |
| 261 void AudioOutputController::DoClose() { | 259 void AudioOutputController::DoClose() { |
| 262 DCHECK(message_loop_->BelongsToCurrentThread()); | 260 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 263 | 261 |
| 264 if (state_ != kClosed) { | 262 if (state_ != kClosed) { |
| 263 DCHECK(!divert_glue_); | |
| 265 DoStopCloseAndClearStream(NULL); | 264 DoStopCloseAndClearStream(NULL); |
| 266 sync_reader_->Close(); | 265 sync_reader_->Close(); |
| 267 state_ = kClosed; | 266 state_ = kClosed; |
| 268 } | 267 } |
| 269 } | 268 } |
| 270 | 269 |
| 271 void AudioOutputController::DoSetVolume(double volume) { | 270 void AudioOutputController::DoSetVolume(double volume) { |
| 272 DCHECK(message_loop_->BelongsToCurrentThread()); | 271 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 273 | 272 |
| 274 // Saves the volume to a member first. We may not be able to set the volume | 273 // Saves the volume to a member first. We may not be able to set the volume |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 287 return; | 286 return; |
| 288 } | 287 } |
| 289 } | 288 } |
| 290 | 289 |
| 291 void AudioOutputController::DoReportError(int code) { | 290 void AudioOutputController::DoReportError(int code) { |
| 292 DCHECK(message_loop_->BelongsToCurrentThread()); | 291 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 293 if (state_ != kClosed) | 292 if (state_ != kClosed) |
| 294 handler_->OnError(this, code); | 293 handler_->OnError(this, code); |
| 295 } | 294 } |
| 296 | 295 |
| 297 int AudioOutputController::OnMoreData(AudioBus* dest, | 296 AudioOutputController::Glue::Glue(AudioOutputController* controller, |
| 298 AudioBuffersState buffers_state) { | 297 SyncReader* source) |
| 299 return OnMoreIOData(NULL, dest, buffers_state); | 298 : controller_(controller), |
| 299 source_(source), | |
| 300 is_reading_from_source_(false) { | |
| 300 } | 301 } |
| 301 | 302 |
| 302 int AudioOutputController::OnMoreIOData(AudioBus* source, | 303 AudioOutputController::Glue::~Glue() { |
| 303 AudioBus* dest, | 304 } |
| 304 AudioBuffersState buffers_state) { | 305 |
| 306 void AudioOutputController::Glue::PassSoon(const scoped_refptr<Glue>& to) { | |
|
DaleCurtis
2012/12/12 01:29:15
This is all really complicated and I don't quite f
miu
2012/12/12 03:13:39
Main reason: Per our hallway conversation, "Though
DaleCurtis
2012/12/12 20:38:38
How come you don't do something similar to OnDevic
miu
2012/12/13 01:22:51
Done.
Good call, Dale! :-) Per hallway conversa
| |
| 307 DCHECK_NE(this, to); | |
| 308 | |
| 309 SyncReader* pass_me; | |
| 310 { | |
| 311 base::AutoLock auto_lock(lock_); | |
| 312 | |
| 313 // Two reasons to delay a pass: 1) another thread is currently reading from | |
| 314 // source_; 2) this instance is hasn't received source_ yet. | |
| 315 if (is_reading_from_source_ || !source_) { | |
| 316 DCHECK(delayed_pass_.is_null()) << "BUG: Attempt to pass twice."; | |
| 317 delayed_pass_ = base::Bind(&Glue::DoDelayedPassWithLockHeld, this, to); | |
| 318 return; | |
| 319 } | |
| 320 | |
| 321 pass_me = source_; | |
| 322 source_ = NULL; | |
| 323 } | |
| 324 | |
| 325 // Normal path: Pass source_ immediately. | |
| 326 to->ReceiveSource(pass_me); | |
| 327 } | |
| 328 | |
| 329 void AudioOutputController::Glue::ReceiveSource(SyncReader* source) { | |
| 330 base::AutoLock auto_lock(lock_); | |
| 331 | |
| 332 // Accept the audio data source. | |
| 333 DCHECK(!source_) | |
| 334 << "BUG: Attempt to receive while already holding a SyncReader."; | |
| 335 source_ = source; | |
| 336 | |
| 337 // Execute a delayed pass if PassSoon() scheduled one. | |
| 338 if (!delayed_pass_.is_null()) { | |
| 339 delayed_pass_.Run(); | |
| 340 delayed_pass_.Reset(); | |
| 341 } | |
| 342 } | |
| 343 | |
| 344 void AudioOutputController::Glue::DoDelayedPassWithLockHeld( | |
| 345 const scoped_refptr<Glue>& to) { | |
| 346 lock_.AssertAcquired(); | |
| 347 | |
| 348 // Pass the SyncReader to another Glue instance, but do not allow the current | |
| 349 // thread to block on this action. | |
| 350 controller_->message_loop_->PostTask( | |
| 351 FROM_HERE, base::Bind(&Glue::ReceiveSource, to, source_)); | |
| 352 source_ = NULL; | |
| 353 } | |
| 354 | |
| 355 int AudioOutputController::Glue::OnMoreData(AudioBus* dest_bus, | |
| 356 AudioBuffersState buffers_state) { | |
| 357 return OnMoreIOData(NULL, dest_bus, buffers_state); | |
| 358 } | |
| 359 | |
| 360 int AudioOutputController::Glue::OnMoreIOData(AudioBus* source_bus, | |
| 361 AudioBus* dest_bus, | |
| 362 AudioBuffersState buffers_state) { | |
| 305 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); | 363 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); |
| 306 | 364 |
| 365 // Attempt to read from the SyncReader. While reading, do not hold lock_. | |
| 366 // After reading, acquire the lock again; and execute a delayed pass, if | |
| 367 // scheduled. | |
| 307 { | 368 { |
| 308 // Check state and do nothing if we are not playing. | |
| 309 // We are on the hardware audio thread, so lock is needed. | |
| 310 base::AutoLock auto_lock(lock_); | 369 base::AutoLock auto_lock(lock_); |
| 311 if (state_ != kPlaying) { | 370 if (source_ && controller_->state_ == kPlaying) { |
| 312 return 0; | 371 is_reading_from_source_ = true; |
| 372 int frames_read; | |
| 373 { | |
| 374 base::AutoUnlock unlock_while_reading(lock_); | |
| 375 frames_read = source_->Read(source_bus, dest_bus); | |
| 376 source_->UpdatePendingBytes( | |
| 377 buffers_state.total_bytes() + | |
| 378 frames_read * controller_->params_.GetBytesPerFrame()); | |
| 379 } | |
| 380 is_reading_from_source_ = false; | |
| 381 if (!delayed_pass_.is_null()) { | |
| 382 delayed_pass_.Run(); | |
| 383 delayed_pass_.Reset(); | |
| 384 } | |
| 385 return frames_read; | |
| 313 } | 386 } |
| 314 } | 387 } |
| 315 | 388 |
| 316 int frames = sync_reader_->Read(source, dest); | 389 // We haven't received the SyncReader yet, so fill the destination with zeros. |
| 317 sync_reader_->UpdatePendingBytes( | 390 dest_bus->Zero(); |
| 318 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); | 391 return dest_bus->frames(); |
| 319 return frames; | |
| 320 } | 392 } |
| 321 | 393 |
| 322 void AudioOutputController::WaitTillDataReady() { | 394 void AudioOutputController::Glue::WaitTillDataReady() { |
| 323 #if defined(OS_WIN) || defined(OS_MACOSX) | 395 #if defined(OS_WIN) || defined(OS_MACOSX) |
| 324 base::Time start = base::Time::Now(); | 396 base::Time start = base::Time::Now(); |
| 325 // Wait for up to 1.5 seconds for DataReady(). 1.5 seconds was chosen because | 397 // Wait for up to 1.5 seconds for DataReady(). 1.5 seconds was chosen because |
| 326 // it's larger than the playback time of the WaveOut buffer size using the | 398 // it's larger than the playback time of the WaveOut buffer size using the |
| 327 // minimum supported sample rate: 4096 / 3000 = ~1.4 seconds. Even a client | 399 // minimum supported sample rate: 4096 / 3000 = ~1.4 seconds. Even a client |
| 328 // expecting real time playout should be able to fill in this time. | 400 // expecting real time playout should be able to fill in this time. |
| 329 const base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(1500); | 401 const base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(1500); |
| 330 while (!sync_reader_->DataReady() && | 402 base::AutoLock auto_lock(lock_); |
| 403 while (source_ && !source_->DataReady() && | |
| 331 ((base::Time::Now() - start) < max_wait)) { | 404 ((base::Time::Now() - start) < max_wait)) { |
| 405 base::AutoUnlock unlock_while_yielding(lock_); | |
| 332 base::PlatformThread::YieldCurrentThread(); | 406 base::PlatformThread::YieldCurrentThread(); |
| 333 } | 407 } |
| 334 #else | 408 #else |
| 335 // WaitTillDataReady() is deprecated and should not be used. | 409 // WaitTillDataReady() is deprecated and should not be used. |
| 336 CHECK(false); | 410 CHECK(false); |
| 337 #endif | 411 #endif |
| 338 } | 412 } |
| 339 | 413 |
| 340 void AudioOutputController::OnError(AudioOutputStream* stream, int code) { | 414 void AudioOutputController::Glue::OnError(AudioOutputStream* stream, int code) { |
| 341 // Handle error on the audio controller thread. | 415 // Handle error on the audio controller thread. |
| 342 message_loop_->PostTask(FROM_HERE, base::Bind( | 416 controller_->message_loop_->PostTask(FROM_HERE, base::Bind( |
| 343 &AudioOutputController::DoReportError, this, code)); | 417 &AudioOutputController::DoReportError, controller_, code)); |
| 344 } | 418 } |
| 345 | 419 |
| 346 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { | 420 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { |
| 347 DCHECK(message_loop_->BelongsToCurrentThread()); | 421 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 348 | 422 |
| 349 // Allow calling unconditionally and bail if we don't have a stream_ to close. | 423 // Allow calling unconditionally and bail if we don't have a stream_ to close. |
| 350 if (stream_) { | 424 if (stream_) { |
| 351 stream_->Stop(); | 425 stream_->Stop(); |
| 352 stream_->Close(); | 426 stream_->Close(); |
| 353 stream_ = NULL; | 427 stream_ = NULL; |
| 354 | 428 |
| 355 audio_manager_->RemoveOutputDeviceChangeListener(this); | 429 audio_manager_->RemoveOutputDeviceChangeListener(this); |
| 356 audio_manager_ = NULL; | |
| 357 | 430 |
| 358 weak_this_.InvalidateWeakPtrs(); | 431 weak_this_.InvalidateWeakPtrs(); |
| 359 } | 432 } |
| 360 | 433 |
| 361 // Should be last in the method, do not touch "this" from here on. | 434 // Should be last in the method, do not touch "this" from here on. |
| 362 if (done) | 435 if (done) |
| 363 done->Signal(); | 436 done->Signal(); |
| 364 } | 437 } |
| 365 | 438 |
| 366 void AudioOutputController::OnDeviceChange() { | 439 void AudioOutputController::OnDeviceChange() { |
| 367 DCHECK(message_loop_->BelongsToCurrentThread()); | 440 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 368 | 441 |
| 369 // We should always have a stream by this point. | 442 // We should always have a stream by this point. |
| 370 CHECK(stream_); | 443 CHECK(stream_); |
| 371 | 444 |
| 372 // Preserve the original state and shutdown the stream. | 445 // Preserve the original state. |
| 373 State original_state = state_; | 446 const State original_state = state_; |
| 374 stream_->Stop(); | |
| 375 stream_->Close(); | |
| 376 stream_ = NULL; | |
| 377 | 447 |
| 378 // Recreate the stream, exit if we ran into an error. | 448 // Recreate the stream (DoCreate() will first shut down an existing stream). |
| 449 // Exit if we ran into an error. | |
| 379 state_ = kRecreating; | 450 state_ = kRecreating; |
| 380 DoCreate(); | 451 DoCreate(); |
| 381 if (!stream_ || state_ == kError) | 452 if (!stream_ || state_ == kError) |
| 382 return; | 453 return; |
| 383 | 454 |
| 384 // Get us back to the original state or an equivalent state. | 455 // Get us back to the original state or an equivalent state. |
| 385 switch (original_state) { | 456 switch (original_state) { |
| 386 case kStarting: | 457 case kStarting: |
| 387 case kPlaying: | 458 case kPlaying: |
| 459 DoSetVolume(volume_); | |
| 388 DoPlay(); | 460 DoPlay(); |
| 389 return; | 461 return; |
| 390 case kCreated: | 462 case kCreated: |
| 391 case kPausedWhenStarting: | 463 case kPausedWhenStarting: |
| 392 case kPaused: | 464 case kPaused: |
| 393 // From the outside these three states are equivalent. | 465 // From the outside these three states are equivalent. |
| 394 return; | 466 return; |
| 395 default: | 467 default: |
| 396 NOTREACHED() << "Invalid original state."; | 468 NOTREACHED() << "Invalid original state."; |
| 397 } | 469 } |
| 398 } | 470 } |
| 399 | 471 |
| 472 AudioOutputStream::AudioSourceCallback* AudioOutputController::Divert() { | |
| 473 DCHECK(!divert_glue_) << "BUG: Already diverted!"; | |
| 474 divert_glue_ = new Glue(this, NULL); | |
| 475 stream_glue_->PassSoon(divert_glue_); | |
| 476 return divert_glue_; | |
| 477 } | |
| 478 | |
| 479 void AudioOutputController::Revert( | |
| 480 AudioOutputStream::AudioSourceCallback* asc) { | |
| 481 DCHECK_EQ(divert_glue_, asc); | |
| 482 divert_glue_->PassSoon(stream_glue_); | |
| 483 divert_glue_ = NULL; | |
| 484 } | |
| 485 | |
| 400 } // namespace media | 486 } // namespace media |
| OLD | NEW |