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