audio_output_controller.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/message_loop/message_loop.h" 10#include "base/metrics/histogram.h" 11#include "base/threading/platform_thread.h" 12#include "base/time/time.h" 13#include "build/build_config.h" 14#include "media/audio/shared_memory_util.h" 15#include "media/base/scoped_histogram_timer.h" 16 17using base::Time; 18using base::TimeDelta; 19 20namespace media { 21 22#if defined(AUDIO_POWER_MONITORING) 23// Time constant for AudioPowerMonitor. See AudioPowerMonitor ctor comments for 24// semantics. This value was arbitrarily chosen, but seems to work well. 25static const int kPowerMeasurementTimeConstantMillis = 10; 26 27// Desired frequency of calls to EventHandler::OnPowerMeasured() for reporting 28// power levels in the audio signal. 29static const int kPowerMeasurementsPerSecond = 4; 30#endif 31 32// Polling-related constants. 33const int AudioOutputController::kPollNumAttempts = 3; 34const int AudioOutputController::kPollPauseInMilliseconds = 3; 35 36AudioOutputController::AudioOutputController( 37 AudioManager* audio_manager, 38 EventHandler* handler, 39 const AudioParameters& params, 40 const std::string& output_device_id, 41 const std::string& input_device_id, 42 SyncReader* sync_reader) 43 : audio_manager_(audio_manager), 44 params_(params), 45 handler_(handler), 46 output_device_id_(output_device_id), 47 input_device_id_(input_device_id), 48 stream_(NULL), 49 diverting_to_stream_(NULL), 50 volume_(1.0), 51 state_(kEmpty), 52 num_allowed_io_(0), 53 sync_reader_(sync_reader), 54 message_loop_(audio_manager->GetMessageLoop()), 55#if defined(AUDIO_POWER_MONITORING) 56 power_monitor_( 57 params.sample_rate(), 58 TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)), 59#endif 60 number_polling_attempts_left_(0) { 61 DCHECK(audio_manager); 62 DCHECK(handler_); 63 DCHECK(sync_reader_); 64 DCHECK(message_loop_.get()); 65} 66 67AudioOutputController::~AudioOutputController() { 68 DCHECK_EQ(kClosed, state_); 69} 70 71// static 72scoped_refptr<AudioOutputController> AudioOutputController::Create( 73 AudioManager* audio_manager, 74 EventHandler* event_handler, 75 const AudioParameters& params, 76 const std::string& output_device_id, 77 const std::string& input_device_id, 78 SyncReader* sync_reader) { 79 DCHECK(audio_manager); 80 DCHECK(sync_reader); 81 82 if (!params.IsValid() || !audio_manager) 83 return NULL; 84 85 scoped_refptr<AudioOutputController> controller(new AudioOutputController( 86 audio_manager, event_handler, params, output_device_id, input_device_id, 87 sync_reader)); 88 controller->message_loop_->PostTask(FROM_HERE, base::Bind( 89 &AudioOutputController::DoCreate, controller, false)); 90 return controller; 91} 92 93void AudioOutputController::Play() { 94 message_loop_->PostTask(FROM_HERE, base::Bind( 95 &AudioOutputController::DoPlay, this)); 96} 97 98void AudioOutputController::Pause() { 99 message_loop_->PostTask(FROM_HERE, base::Bind( 100 &AudioOutputController::DoPause, this)); 101} 102 103void AudioOutputController::Close(const base::Closure& closed_task) { 104 DCHECK(!closed_task.is_null()); 105 message_loop_->PostTaskAndReply(FROM_HERE, base::Bind( 106 &AudioOutputController::DoClose, this), closed_task); 107} 108 109void AudioOutputController::SetVolume(double volume) { 110 message_loop_->PostTask(FROM_HERE, base::Bind( 111 &AudioOutputController::DoSetVolume, this, volume)); 112} 113 114void AudioOutputController::DoCreate(bool is_for_device_change) { 115 DCHECK(message_loop_->BelongsToCurrentThread()); 116 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime"); 117 118 // Close() can be called before DoCreate() is executed. 119 if (state_ == kClosed) 120 return; 121 122 DoStopCloseAndClearStream(); // Calls RemoveOutputDeviceChangeListener(). 123 DCHECK_EQ(kEmpty, state_); 124 125 stream_ = diverting_to_stream_ ? 126 diverting_to_stream_ : 127 audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_, 128 input_device_id_); 129 if (!stream_) { 130 state_ = kError; 131 handler_->OnError(); 132 return; 133 } 134 135 if (!stream_->Open()) { 136 DoStopCloseAndClearStream(); 137 state_ = kError; 138 handler_->OnError(); 139 return; 140 } 141 142 // Everything started okay, so re-register for state change callbacks if 143 // stream_ was created via AudioManager. 144 if (stream_ != diverting_to_stream_) 145 audio_manager_->AddOutputDeviceChangeListener(this); 146 147 // We have successfully opened the stream. Set the initial volume. 148 stream_->SetVolume(volume_); 149 150 // Finally set the state to kCreated. 151 state_ = kCreated; 152 153 // And then report we have been created if we haven't done so already. 154 if (!is_for_device_change) 155 handler_->OnCreated(); 156} 157 158void AudioOutputController::DoPlay() { 159 DCHECK(message_loop_->BelongsToCurrentThread()); 160 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); 161 162 // We can start from created or paused state. 163 if (state_ != kCreated && state_ != kPaused) 164 return; 165 166 // Ask for first packet. 167 sync_reader_->UpdatePendingBytes(0); 168 169 state_ = kPlaying; 170 171#if defined(AUDIO_POWER_MONITORING) 172 power_monitor_.Reset(); 173 power_poll_callback_.Reset( 174 base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically, 175 this)); 176 // Run the callback to send an initial notification that we're starting in 177 // silence, and to schedule periodic callbacks. 178 power_poll_callback_.callback().Run(); 179#endif 180 181 // We start the AudioOutputStream lazily. 182 AllowEntryToOnMoreIOData(); 183 stream_->Start(this); 184 185 handler_->OnPlaying(); 186} 187 188#if defined(AUDIO_POWER_MONITORING) 189void AudioOutputController::ReportPowerMeasurementPeriodically() { 190 DCHECK(message_loop_->BelongsToCurrentThread()); 191 const std::pair<float, bool>& reading = 192 power_monitor_.ReadCurrentPowerAndClip(); 193 handler_->OnPowerMeasured(reading.first, reading.second); 194 message_loop_->PostDelayedTask( 195 FROM_HERE, power_poll_callback_.callback(), 196 TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond); 197} 198#endif 199 200void AudioOutputController::StopStream() { 201 DCHECK(message_loop_->BelongsToCurrentThread()); 202 203 if (state_ == kPlaying) { 204 stream_->Stop(); 205 DisallowEntryToOnMoreIOData(); 206 207#if defined(AUDIO_POWER_MONITORING) 208 power_poll_callback_.Cancel(); 209#endif 210 211 state_ = kPaused; 212 } 213} 214 215void AudioOutputController::DoPause() { 216 DCHECK(message_loop_->BelongsToCurrentThread()); 217 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); 218 219 StopStream(); 220 221 if (state_ != kPaused) 222 return; 223 224 // Send a special pause mark to the low-latency audio thread. 225 sync_reader_->UpdatePendingBytes(kPauseMark); 226 227#if defined(AUDIO_POWER_MONITORING) 228 // Paused means silence follows. 229 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); 230#endif 231 232 handler_->OnPaused(); 233} 234 235void AudioOutputController::DoClose() { 236 DCHECK(message_loop_->BelongsToCurrentThread()); 237 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); 238 239 if (state_ != kClosed) { 240 DoStopCloseAndClearStream(); 241 sync_reader_->Close(); 242 state_ = kClosed; 243 } 244} 245 246void AudioOutputController::DoSetVolume(double volume) { 247 DCHECK(message_loop_->BelongsToCurrentThread()); 248 249 // Saves the volume to a member first. We may not be able to set the volume 250 // right away but when the stream is created we'll set the volume. 251 volume_ = volume; 252 253 switch (state_) { 254 case kCreated: 255 case kPlaying: 256 case kPaused: 257 stream_->SetVolume(volume_); 258 break; 259 default: 260 return; 261 } 262} 263 264void AudioOutputController::DoReportError() { 265 DCHECK(message_loop_->BelongsToCurrentThread()); 266 if (state_ != kClosed) 267 handler_->OnError(); 268} 269 270int AudioOutputController::OnMoreData(AudioBus* dest, 271 AudioBuffersState buffers_state) { 272 return OnMoreIOData(NULL, dest, buffers_state); 273} 274 275int AudioOutputController::OnMoreIOData(AudioBus* source, 276 AudioBus* dest, 277 AudioBuffersState buffers_state) { 278 DisallowEntryToOnMoreIOData(); 279 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); 280 281 // The OS level audio APIs on Linux and Windows all have problems requesting 282 // data on a fixed interval. Sometimes they will issue calls back to back 283 // which can cause glitching, so wait until the renderer is ready. 284 // 285 // We also need to wait when diverting since the virtual stream will call this 286 // multiple times without waiting. 287 // 288 // NEVER wait on OSX unless a virtual stream is connected, otherwise we can 289 // end up hanging the entire OS. 290 // 291 // See many bugs for context behind this decision: http://crbug.com/170498, 292 // http://crbug.com/171651, http://crbug.com/174985, and more. 293#if defined(OS_WIN) || defined(OS_LINUX) 294 const bool kShouldBlock = true; 295#else 296 const bool kShouldBlock = diverting_to_stream_ != NULL; 297#endif 298 299 const int frames = sync_reader_->Read(kShouldBlock, source, dest); 300 DCHECK_LE(0, frames); 301 sync_reader_->UpdatePendingBytes( 302 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); 303 304#if defined(AUDIO_POWER_MONITORING) 305 power_monitor_.Scan(*dest, frames); 306#endif 307 308 AllowEntryToOnMoreIOData(); 309 return frames; 310} 311 312void AudioOutputController::OnError(AudioOutputStream* stream) { 313 // Handle error on the audio controller thread. 314 message_loop_->PostTask(FROM_HERE, base::Bind( 315 &AudioOutputController::DoReportError, this)); 316} 317 318void AudioOutputController::DoStopCloseAndClearStream() { 319 DCHECK(message_loop_->BelongsToCurrentThread()); 320 321 // Allow calling unconditionally and bail if we don't have a stream_ to close. 322 if (stream_) { 323 // De-register from state change callbacks if stream_ was created via 324 // AudioManager. 325 if (stream_ != diverting_to_stream_) 326 audio_manager_->RemoveOutputDeviceChangeListener(this); 327 328 StopStream(); 329 stream_->Close(); 330 if (stream_ == diverting_to_stream_) 331 diverting_to_stream_ = NULL; 332 stream_ = NULL; 333 } 334 335 state_ = kEmpty; 336} 337 338void AudioOutputController::OnDeviceChange() { 339 DCHECK(message_loop_->BelongsToCurrentThread()); 340 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); 341 342 // TODO(dalecurtis): Notify the renderer side that a device change has 343 // occurred. Currently querying the hardware information here will lead to 344 // crashes on OSX. See http://crbug.com/158170. 345 346 // Recreate the stream (DoCreate() will first shut down an existing stream). 347 // Exit if we ran into an error. 348 const State original_state = state_; 349 DoCreate(true); 350 if (!stream_ || state_ == kError) 351 return; 352 353 // Get us back to the original state or an equivalent state. 354 switch (original_state) { 355 case kPlaying: 356 DoPlay(); 357 return; 358 case kCreated: 359 case kPaused: 360 // From the outside these two states are equivalent. 361 return; 362 default: 363 NOTREACHED() << "Invalid original state."; 364 } 365} 366 367const AudioParameters& AudioOutputController::GetAudioParameters() { 368 return params_; 369} 370 371void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) { 372 message_loop_->PostTask( 373 FROM_HERE, 374 base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream)); 375} 376 377void AudioOutputController::StopDiverting() { 378 message_loop_->PostTask( 379 FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this)); 380} 381 382void AudioOutputController::DoStartDiverting(AudioOutputStream* to_stream) { 383 DCHECK(message_loop_->BelongsToCurrentThread()); 384 385 if (state_ == kClosed) 386 return; 387 388 DCHECK(!diverting_to_stream_); 389 diverting_to_stream_ = to_stream; 390 // Note: OnDeviceChange() will engage the "re-create" process, which will 391 // detect and use the alternate AudioOutputStream rather than create a new one 392 // via AudioManager. 393 OnDeviceChange(); 394} 395 396void AudioOutputController::DoStopDiverting() { 397 DCHECK(message_loop_->BelongsToCurrentThread()); 398 399 if (state_ == kClosed) 400 return; 401 402 // Note: OnDeviceChange() will cause the existing stream (the consumer of the 403 // diverted audio data) to be closed, and diverting_to_stream_ will be set 404 // back to NULL. 405 OnDeviceChange(); 406 DCHECK(!diverting_to_stream_); 407} 408 409void AudioOutputController::AllowEntryToOnMoreIOData() { 410 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); 411 base::AtomicRefCountInc(&num_allowed_io_); 412} 413 414void AudioOutputController::DisallowEntryToOnMoreIOData() { 415 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); 416 DCHECK(is_zero); 417} 418 419} // namespace media 420