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