audio_output_controller.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_output_controller.h" 6 7#include "base/bind.h" 8#include "base/debug/trace_event.h" 9#include "base/metrics/histogram.h" 10#include "base/task_runner_util.h" 11#include "base/threading/platform_thread.h" 12#include "base/time/time.h" 13#include "build/build_config.h" 14#include "media/base/scoped_histogram_timer.h" 15 16using base::TimeDelta; 17 18namespace media { 19 20#if defined(AUDIO_POWER_MONITORING) 21// Time constant for AudioPowerMonitor. See AudioPowerMonitor ctor comments for 22// semantics. This value was arbitrarily chosen, but seems to work well. 23static const int kPowerMeasurementTimeConstantMillis = 10; 24 25// Desired frequency of calls to EventHandler::OnPowerMeasured() for reporting 26// power levels in the audio signal. 27static const int kPowerMeasurementsPerSecond = 4; 28#endif 29 30// Polling-related constants. 31const int AudioOutputController::kPollNumAttempts = 3; 32const int AudioOutputController::kPollPauseInMilliseconds = 3; 33 34AudioOutputController::AudioOutputController( 35 AudioManager* audio_manager, 36 EventHandler* handler, 37 const AudioParameters& params, 38 const std::string& output_device_id, 39 SyncReader* sync_reader) 40 : audio_manager_(audio_manager), 41 params_(params), 42 handler_(handler), 43 output_device_id_(output_device_id), 44 stream_(NULL), 45 diverting_to_stream_(NULL), 46 volume_(1.0), 47 state_(kEmpty), 48 num_allowed_io_(0), 49 sync_reader_(sync_reader), 50 message_loop_(audio_manager->GetTaskRunner()), 51#if defined(AUDIO_POWER_MONITORING) 52 power_monitor_( 53 params.sample_rate(), 54 TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)), 55#endif 56 on_more_io_data_called_(0) { 57 DCHECK(audio_manager); 58 DCHECK(handler_); 59 DCHECK(sync_reader_); 60 DCHECK(message_loop_.get()); 61} 62 63AudioOutputController::~AudioOutputController() { 64 DCHECK_EQ(kClosed, state_); 65} 66 67// static 68scoped_refptr<AudioOutputController> AudioOutputController::Create( 69 AudioManager* audio_manager, 70 EventHandler* event_handler, 71 const AudioParameters& params, 72 const std::string& output_device_id, 73 SyncReader* sync_reader) { 74 DCHECK(audio_manager); 75 DCHECK(sync_reader); 76 77 if (!params.IsValid() || !audio_manager) 78 return NULL; 79 80 scoped_refptr<AudioOutputController> controller(new AudioOutputController( 81 audio_manager, event_handler, params, output_device_id, sync_reader)); 82 controller->message_loop_->PostTask(FROM_HERE, base::Bind( 83 &AudioOutputController::DoCreate, controller, false)); 84 return controller; 85} 86 87void AudioOutputController::Play() { 88 message_loop_->PostTask(FROM_HERE, base::Bind( 89 &AudioOutputController::DoPlay, this)); 90} 91 92void AudioOutputController::Pause() { 93 message_loop_->PostTask(FROM_HERE, base::Bind( 94 &AudioOutputController::DoPause, this)); 95} 96 97void AudioOutputController::Close(const base::Closure& closed_task) { 98 DCHECK(!closed_task.is_null()); 99 message_loop_->PostTaskAndReply(FROM_HERE, base::Bind( 100 &AudioOutputController::DoClose, this), closed_task); 101} 102 103void AudioOutputController::SetVolume(double volume) { 104 message_loop_->PostTask(FROM_HERE, base::Bind( 105 &AudioOutputController::DoSetVolume, this, volume)); 106} 107 108void AudioOutputController::GetOutputDeviceId( 109 base::Callback<void(const std::string&)> callback) const { 110 base::PostTaskAndReplyWithResult( 111 message_loop_.get(), 112 FROM_HERE, 113 base::Bind(&AudioOutputController::DoGetOutputDeviceId, this), 114 callback); 115} 116 117void AudioOutputController::SwitchOutputDevice( 118 const std::string& output_device_id, const base::Closure& callback) { 119 message_loop_->PostTaskAndReply( 120 FROM_HERE, 121 base::Bind(&AudioOutputController::DoSwitchOutputDevice, this, 122 output_device_id), 123 callback); 124} 125 126void AudioOutputController::DoCreate(bool is_for_device_change) { 127 DCHECK(message_loop_->BelongsToCurrentThread()); 128 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime"); 129 TRACE_EVENT0("audio", "AudioOutputController::DoCreate"); 130 131 // Close() can be called before DoCreate() is executed. 132 if (state_ == kClosed) 133 return; 134 135 DoStopCloseAndClearStream(); // Calls RemoveOutputDeviceChangeListener(). 136 DCHECK_EQ(kEmpty, state_); 137 138 stream_ = diverting_to_stream_ ? 139 diverting_to_stream_ : 140 audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_); 141 if (!stream_) { 142 state_ = kError; 143 handler_->OnError(); 144 return; 145 } 146 147 if (!stream_->Open()) { 148 DoStopCloseAndClearStream(); 149 state_ = kError; 150 handler_->OnError(); 151 return; 152 } 153 154 // Everything started okay, so re-register for state change callbacks if 155 // stream_ was created via AudioManager. 156 if (stream_ != diverting_to_stream_) 157 audio_manager_->AddOutputDeviceChangeListener(this); 158 159 // We have successfully opened the stream. Set the initial volume. 160 stream_->SetVolume(volume_); 161 162 // Finally set the state to kCreated. 163 state_ = kCreated; 164 165 // And then report we have been created if we haven't done so already. 166 if (!is_for_device_change) 167 handler_->OnCreated(); 168} 169 170void AudioOutputController::DoPlay() { 171 DCHECK(message_loop_->BelongsToCurrentThread()); 172 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); 173 TRACE_EVENT0("audio", "AudioOutputController::DoPlay"); 174 175 // We can start from created or paused state. 176 if (state_ != kCreated && state_ != kPaused) 177 return; 178 179 // Ask for first packet. 180 sync_reader_->UpdatePendingBytes(0); 181 182 state_ = kPlaying; 183 184#if defined(AUDIO_POWER_MONITORING) 185 power_monitor_.Reset(); 186 power_poll_callback_.Reset( 187 base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically, 188 this)); 189 // Run the callback to send an initial notification that we're starting in 190 // silence, and to schedule periodic callbacks. 191 power_poll_callback_.callback().Run(); 192#endif 193 194 on_more_io_data_called_ = 0; 195 AllowEntryToOnMoreIOData(); 196 stream_->Start(this); 197 198 // For UMA tracking purposes, start the wedge detection timer. This allows us 199 // to record statistics about the number of wedged playbacks in the field. 200 // 201 // WedgeCheck() will look to see if |on_more_io_data_called_| is true after 202 // the timeout expires. Care must be taken to ensure the wedge check delay is 203 // large enough that the value isn't queried while OnMoreDataIO() is setting 204 // it. 205 // 206 // Timer self-manages its lifetime and WedgeCheck() will only record the UMA 207 // statistic if state is still kPlaying. Additional Start() calls will 208 // invalidate the previous timer. 209 wedge_timer_.reset(new base::OneShotTimer<AudioOutputController>()); 210 wedge_timer_->Start( 211 FROM_HERE, TimeDelta::FromSeconds(5), this, 212 &AudioOutputController::WedgeCheck); 213 214 handler_->OnPlaying(); 215} 216 217#if defined(AUDIO_POWER_MONITORING) 218void AudioOutputController::ReportPowerMeasurementPeriodically() { 219 DCHECK(message_loop_->BelongsToCurrentThread()); 220 const std::pair<float, bool>& reading = 221 power_monitor_.ReadCurrentPowerAndClip(); 222 handler_->OnPowerMeasured(reading.first, reading.second); 223 message_loop_->PostDelayedTask( 224 FROM_HERE, power_poll_callback_.callback(), 225 TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond); 226} 227#endif 228 229void AudioOutputController::StopStream() { 230 DCHECK(message_loop_->BelongsToCurrentThread()); 231 232 if (state_ == kPlaying) { 233 wedge_timer_.reset(); 234 stream_->Stop(); 235 DisallowEntryToOnMoreIOData(); 236 237#if defined(AUDIO_POWER_MONITORING) 238 power_poll_callback_.Cancel(); 239#endif 240 241 state_ = kPaused; 242 } 243} 244 245void AudioOutputController::DoPause() { 246 DCHECK(message_loop_->BelongsToCurrentThread()); 247 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); 248 TRACE_EVENT0("audio", "AudioOutputController::DoPause"); 249 250 StopStream(); 251 252 if (state_ != kPaused) 253 return; 254 255 // Let the renderer know we've stopped. Necessary to let PPAPI clients know 256 // audio has been shutdown. TODO(dalecurtis): This stinks. PPAPI should have 257 // a better way to know when it should exit PPB_Audio_Shared::Run(). 258 sync_reader_->UpdatePendingBytes(-1); 259 260#if defined(AUDIO_POWER_MONITORING) 261 // Paused means silence follows. 262 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); 263#endif 264 265 handler_->OnPaused(); 266} 267 268void AudioOutputController::DoClose() { 269 DCHECK(message_loop_->BelongsToCurrentThread()); 270 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); 271 TRACE_EVENT0("audio", "AudioOutputController::DoClose"); 272 273 if (state_ != kClosed) { 274 DoStopCloseAndClearStream(); 275 sync_reader_->Close(); 276 state_ = kClosed; 277 } 278} 279 280void AudioOutputController::DoSetVolume(double volume) { 281 DCHECK(message_loop_->BelongsToCurrentThread()); 282 283 // Saves the volume to a member first. We may not be able to set the volume 284 // right away but when the stream is created we'll set the volume. 285 volume_ = volume; 286 287 switch (state_) { 288 case kCreated: 289 case kPlaying: 290 case kPaused: 291 stream_->SetVolume(volume_); 292 break; 293 default: 294 return; 295 } 296} 297 298std::string AudioOutputController::DoGetOutputDeviceId() const { 299 DCHECK(message_loop_->BelongsToCurrentThread()); 300 return output_device_id_; 301} 302 303void AudioOutputController::DoSwitchOutputDevice( 304 const std::string& output_device_id) { 305 DCHECK(message_loop_->BelongsToCurrentThread()); 306 307 if (state_ == kClosed) 308 return; 309 310 if (output_device_id == output_device_id_) 311 return; 312 313 output_device_id_ = output_device_id; 314 315 // If output is currently diverted, we must not call OnDeviceChange 316 // since it would break the diverted setup. Once diversion is 317 // finished using StopDiverting() the output will switch to the new 318 // device ID. 319 if (stream_ != diverting_to_stream_) 320 OnDeviceChange(); 321} 322 323void AudioOutputController::DoReportError() { 324 DCHECK(message_loop_->BelongsToCurrentThread()); 325 if (state_ != kClosed) 326 handler_->OnError(); 327} 328 329int AudioOutputController::OnMoreData(AudioBus* dest, 330 AudioBuffersState buffers_state) { 331 return OnMoreIOData(NULL, dest, buffers_state); 332} 333 334int AudioOutputController::OnMoreIOData(AudioBus* source, 335 AudioBus* dest, 336 AudioBuffersState buffers_state) { 337 DisallowEntryToOnMoreIOData(); 338 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); 339 340 // Indicate that we haven't wedged (at least not indefinitely, WedgeCheck() 341 // may have already fired if OnMoreIOData() took an abnormal amount of time). 342 // Since this thread is the only writer of |on_more_io_data_called_| once the 343 // thread starts, its safe to compare and then increment. 344 if (base::AtomicRefCountIsZero(&on_more_io_data_called_)) 345 base::AtomicRefCountInc(&on_more_io_data_called_); 346 347 sync_reader_->Read(source, dest); 348 349 const int frames = dest->frames(); 350 sync_reader_->UpdatePendingBytes( 351 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); 352 353#if defined(AUDIO_POWER_MONITORING) 354 power_monitor_.Scan(*dest, frames); 355#endif 356 357 AllowEntryToOnMoreIOData(); 358 return frames; 359} 360 361void AudioOutputController::OnError(AudioOutputStream* stream) { 362 // Handle error on the audio controller thread. 363 message_loop_->PostTask(FROM_HERE, base::Bind( 364 &AudioOutputController::DoReportError, this)); 365} 366 367void AudioOutputController::DoStopCloseAndClearStream() { 368 DCHECK(message_loop_->BelongsToCurrentThread()); 369 370 // Allow calling unconditionally and bail if we don't have a stream_ to close. 371 if (stream_) { 372 // De-register from state change callbacks if stream_ was created via 373 // AudioManager. 374 if (stream_ != diverting_to_stream_) 375 audio_manager_->RemoveOutputDeviceChangeListener(this); 376 377 StopStream(); 378 stream_->Close(); 379 if (stream_ == diverting_to_stream_) 380 diverting_to_stream_ = NULL; 381 stream_ = NULL; 382 } 383 384 state_ = kEmpty; 385} 386 387void AudioOutputController::OnDeviceChange() { 388 DCHECK(message_loop_->BelongsToCurrentThread()); 389 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); 390 TRACE_EVENT0("audio", "AudioOutputController::OnDeviceChange"); 391 392 // TODO(dalecurtis): Notify the renderer side that a device change has 393 // occurred. Currently querying the hardware information here will lead to 394 // crashes on OSX. See http://crbug.com/158170. 395 396 // Recreate the stream (DoCreate() will first shut down an existing stream). 397 // Exit if we ran into an error. 398 const State original_state = state_; 399 DoCreate(true); 400 if (!stream_ || state_ == kError) 401 return; 402 403 // Get us back to the original state or an equivalent state. 404 switch (original_state) { 405 case kPlaying: 406 DoPlay(); 407 return; 408 case kCreated: 409 case kPaused: 410 // From the outside these two states are equivalent. 411 return; 412 default: 413 NOTREACHED() << "Invalid original state."; 414 } 415} 416 417const AudioParameters& AudioOutputController::GetAudioParameters() { 418 return params_; 419} 420 421void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) { 422 message_loop_->PostTask( 423 FROM_HERE, 424 base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream)); 425} 426 427void AudioOutputController::StopDiverting() { 428 message_loop_->PostTask( 429 FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this)); 430} 431 432void AudioOutputController::DoStartDiverting(AudioOutputStream* to_stream) { 433 DCHECK(message_loop_->BelongsToCurrentThread()); 434 435 if (state_ == kClosed) 436 return; 437 438 DCHECK(!diverting_to_stream_); 439 diverting_to_stream_ = to_stream; 440 // Note: OnDeviceChange() will engage the "re-create" process, which will 441 // detect and use the alternate AudioOutputStream rather than create a new one 442 // via AudioManager. 443 OnDeviceChange(); 444} 445 446void AudioOutputController::DoStopDiverting() { 447 DCHECK(message_loop_->BelongsToCurrentThread()); 448 449 if (state_ == kClosed) 450 return; 451 452 // Note: OnDeviceChange() will cause the existing stream (the consumer of the 453 // diverted audio data) to be closed, and diverting_to_stream_ will be set 454 // back to NULL. 455 OnDeviceChange(); 456 DCHECK(!diverting_to_stream_); 457} 458 459void AudioOutputController::AllowEntryToOnMoreIOData() { 460 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); 461 base::AtomicRefCountInc(&num_allowed_io_); 462} 463 464void AudioOutputController::DisallowEntryToOnMoreIOData() { 465 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); 466 DCHECK(is_zero); 467} 468 469void AudioOutputController::WedgeCheck() { 470 DCHECK(message_loop_->BelongsToCurrentThread()); 471 472 // If we should be playing and we haven't, that's a wedge. 473 if (state_ == kPlaying) { 474 const bool playback_success = 475 base::AtomicRefCountIsOne(&on_more_io_data_called_); 476 477 UMA_HISTOGRAM_BOOLEAN( 478 "Media.AudioOutputControllerPlaybackStartupSuccess", playback_success); 479 480 // Let the AudioManager try and fix it. 481 if (!playback_success) 482 audio_manager_->FixWedgedAudio(); 483 } 484} 485 486} // namespace media 487