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