audio_output_controller.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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/task_runner_util.h" 12#include "base/threading/platform_thread.h" 13#include "base/time/time.h" 14#include "build/build_config.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::GetOutputDeviceId( 115 base::Callback<void(const std::string&)> callback) const { 116 base::PostTaskAndReplyWithResult( 117 message_loop_.get(), 118 FROM_HERE, 119 base::Bind(&AudioOutputController::DoGetOutputDeviceId, this), 120 callback); 121} 122 123void AudioOutputController::SwitchOutputDevice( 124 const std::string& output_device_id, const base::Closure& callback) { 125 message_loop_->PostTaskAndReply( 126 FROM_HERE, 127 base::Bind(&AudioOutputController::DoSwitchOutputDevice, this, 128 output_device_id), 129 callback); 130} 131 132void AudioOutputController::DoCreate(bool is_for_device_change) { 133 DCHECK(message_loop_->BelongsToCurrentThread()); 134 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime"); 135 136 // Close() can be called before DoCreate() is executed. 137 if (state_ == kClosed) 138 return; 139 140 DoStopCloseAndClearStream(); // Calls RemoveOutputDeviceChangeListener(). 141 DCHECK_EQ(kEmpty, state_); 142 143 stream_ = diverting_to_stream_ ? 144 diverting_to_stream_ : 145 audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_, 146 input_device_id_); 147 if (!stream_) { 148 state_ = kError; 149 handler_->OnError(); 150 return; 151 } 152 153 if (!stream_->Open()) { 154 DoStopCloseAndClearStream(); 155 state_ = kError; 156 handler_->OnError(); 157 return; 158 } 159 160 // Everything started okay, so re-register for state change callbacks if 161 // stream_ was created via AudioManager. 162 if (stream_ != diverting_to_stream_) 163 audio_manager_->AddOutputDeviceChangeListener(this); 164 165 // We have successfully opened the stream. Set the initial volume. 166 stream_->SetVolume(volume_); 167 168 // Finally set the state to kCreated. 169 state_ = kCreated; 170 171 // And then report we have been created if we haven't done so already. 172 if (!is_for_device_change) 173 handler_->OnCreated(); 174} 175 176void AudioOutputController::DoPlay() { 177 DCHECK(message_loop_->BelongsToCurrentThread()); 178 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); 179 180 // We can start from created or paused state. 181 if (state_ != kCreated && state_ != kPaused) 182 return; 183 184 // Ask for first packet. 185 sync_reader_->UpdatePendingBytes(0); 186 187 state_ = kPlaying; 188 189#if defined(AUDIO_POWER_MONITORING) 190 power_monitor_.Reset(); 191 power_poll_callback_.Reset( 192 base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically, 193 this)); 194 // Run the callback to send an initial notification that we're starting in 195 // silence, and to schedule periodic callbacks. 196 power_poll_callback_.callback().Run(); 197#endif 198 199 // We start the AudioOutputStream lazily. 200 AllowEntryToOnMoreIOData(); 201 stream_->Start(this); 202 203 handler_->OnPlaying(); 204} 205 206#if defined(AUDIO_POWER_MONITORING) 207void AudioOutputController::ReportPowerMeasurementPeriodically() { 208 DCHECK(message_loop_->BelongsToCurrentThread()); 209 const std::pair<float, bool>& reading = 210 power_monitor_.ReadCurrentPowerAndClip(); 211 handler_->OnPowerMeasured(reading.first, reading.second); 212 message_loop_->PostDelayedTask( 213 FROM_HERE, power_poll_callback_.callback(), 214 TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond); 215} 216#endif 217 218void AudioOutputController::StopStream() { 219 DCHECK(message_loop_->BelongsToCurrentThread()); 220 221 if (state_ == kPlaying) { 222 stream_->Stop(); 223 DisallowEntryToOnMoreIOData(); 224 225#if defined(AUDIO_POWER_MONITORING) 226 power_poll_callback_.Cancel(); 227#endif 228 229 state_ = kPaused; 230 } 231} 232 233void AudioOutputController::DoPause() { 234 DCHECK(message_loop_->BelongsToCurrentThread()); 235 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); 236 237 StopStream(); 238 239 if (state_ != kPaused) 240 return; 241 242 // Let the renderer know we've stopped. Necessary to let PPAPI clients know 243 // audio has been shutdown. TODO(dalecurtis): This stinks. PPAPI should have 244 // a better way to know when it should exit PPB_Audio_Shared::Run(). 245 sync_reader_->UpdatePendingBytes(-1); 246 247#if defined(AUDIO_POWER_MONITORING) 248 // Paused means silence follows. 249 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); 250#endif 251 252 handler_->OnPaused(); 253} 254 255void AudioOutputController::DoClose() { 256 DCHECK(message_loop_->BelongsToCurrentThread()); 257 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); 258 259 if (state_ != kClosed) { 260 DoStopCloseAndClearStream(); 261 sync_reader_->Close(); 262 state_ = kClosed; 263 } 264} 265 266void AudioOutputController::DoSetVolume(double volume) { 267 DCHECK(message_loop_->BelongsToCurrentThread()); 268 269 // Saves the volume to a member first. We may not be able to set the volume 270 // right away but when the stream is created we'll set the volume. 271 volume_ = volume; 272 273 switch (state_) { 274 case kCreated: 275 case kPlaying: 276 case kPaused: 277 stream_->SetVolume(volume_); 278 break; 279 default: 280 return; 281 } 282} 283 284std::string AudioOutputController::DoGetOutputDeviceId() const { 285 DCHECK(message_loop_->BelongsToCurrentThread()); 286 return output_device_id_; 287} 288 289void AudioOutputController::DoSwitchOutputDevice( 290 const std::string& output_device_id) { 291 DCHECK(message_loop_->BelongsToCurrentThread()); 292 293 if (state_ == kClosed) 294 return; 295 296 output_device_id_ = output_device_id; 297 298 // If output is currently diverted, we must not call OnDeviceChange 299 // since it would break the diverted setup. Once diversion is 300 // finished using StopDiverting() the output will switch to the new 301 // device ID. 302 if (stream_ != diverting_to_stream_) 303 OnDeviceChange(); 304} 305 306void AudioOutputController::DoReportError() { 307 DCHECK(message_loop_->BelongsToCurrentThread()); 308 if (state_ != kClosed) 309 handler_->OnError(); 310} 311 312int AudioOutputController::OnMoreData(AudioBus* dest, 313 AudioBuffersState buffers_state) { 314 return OnMoreIOData(NULL, dest, buffers_state); 315} 316 317int AudioOutputController::OnMoreIOData(AudioBus* source, 318 AudioBus* dest, 319 AudioBuffersState buffers_state) { 320 DisallowEntryToOnMoreIOData(); 321 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); 322 323 sync_reader_->Read(source, dest); 324 325 const int frames = dest->frames(); 326 sync_reader_->UpdatePendingBytes( 327 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); 328 329#if defined(AUDIO_POWER_MONITORING) 330 power_monitor_.Scan(*dest, frames); 331#endif 332 333 AllowEntryToOnMoreIOData(); 334 return frames; 335} 336 337void AudioOutputController::OnError(AudioOutputStream* stream) { 338 // Handle error on the audio controller thread. 339 message_loop_->PostTask(FROM_HERE, base::Bind( 340 &AudioOutputController::DoReportError, this)); 341} 342 343void AudioOutputController::DoStopCloseAndClearStream() { 344 DCHECK(message_loop_->BelongsToCurrentThread()); 345 346 // Allow calling unconditionally and bail if we don't have a stream_ to close. 347 if (stream_) { 348 // De-register from state change callbacks if stream_ was created via 349 // AudioManager. 350 if (stream_ != diverting_to_stream_) 351 audio_manager_->RemoveOutputDeviceChangeListener(this); 352 353 StopStream(); 354 stream_->Close(); 355 if (stream_ == diverting_to_stream_) 356 diverting_to_stream_ = NULL; 357 stream_ = NULL; 358 } 359 360 state_ = kEmpty; 361} 362 363void AudioOutputController::OnDeviceChange() { 364 DCHECK(message_loop_->BelongsToCurrentThread()); 365 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); 366 367 // TODO(dalecurtis): Notify the renderer side that a device change has 368 // occurred. Currently querying the hardware information here will lead to 369 // crashes on OSX. See http://crbug.com/158170. 370 371 // Recreate the stream (DoCreate() will first shut down an existing stream). 372 // Exit if we ran into an error. 373 const State original_state = state_; 374 DoCreate(true); 375 if (!stream_ || state_ == kError) 376 return; 377 378 // Get us back to the original state or an equivalent state. 379 switch (original_state) { 380 case kPlaying: 381 DoPlay(); 382 return; 383 case kCreated: 384 case kPaused: 385 // From the outside these two states are equivalent. 386 return; 387 default: 388 NOTREACHED() << "Invalid original state."; 389 } 390} 391 392const AudioParameters& AudioOutputController::GetAudioParameters() { 393 return params_; 394} 395 396void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) { 397 message_loop_->PostTask( 398 FROM_HERE, 399 base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream)); 400} 401 402void AudioOutputController::StopDiverting() { 403 message_loop_->PostTask( 404 FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this)); 405} 406 407void AudioOutputController::DoStartDiverting(AudioOutputStream* to_stream) { 408 DCHECK(message_loop_->BelongsToCurrentThread()); 409 410 if (state_ == kClosed) 411 return; 412 413 DCHECK(!diverting_to_stream_); 414 diverting_to_stream_ = to_stream; 415 // Note: OnDeviceChange() will engage the "re-create" process, which will 416 // detect and use the alternate AudioOutputStream rather than create a new one 417 // via AudioManager. 418 OnDeviceChange(); 419} 420 421void AudioOutputController::DoStopDiverting() { 422 DCHECK(message_loop_->BelongsToCurrentThread()); 423 424 if (state_ == kClosed) 425 return; 426 427 // Note: OnDeviceChange() will cause the existing stream (the consumer of the 428 // diverted audio data) to be closed, and diverting_to_stream_ will be set 429 // back to NULL. 430 OnDeviceChange(); 431 DCHECK(!diverting_to_stream_); 432} 433 434void AudioOutputController::AllowEntryToOnMoreIOData() { 435 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); 436 base::AtomicRefCountInc(&num_allowed_io_); 437} 438 439void AudioOutputController::DisallowEntryToOnMoreIOData() { 440 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); 441 DCHECK(is_zero); 442} 443 444} // namespace media 445