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