Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/audio_output_controller.h" | 5 #include "media/audio/audio_output_controller.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
| 9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 50 volume_(1.0), | 50 volume_(1.0), |
| 51 state_(kEmpty), | 51 state_(kEmpty), |
| 52 num_allowed_io_(0), | 52 num_allowed_io_(0), |
| 53 sync_reader_(sync_reader), | 53 sync_reader_(sync_reader), |
| 54 message_loop_(audio_manager->GetMessageLoop()), | 54 message_loop_(audio_manager->GetMessageLoop()), |
| 55 #if defined(AUDIO_POWER_MONITORING) | 55 #if defined(AUDIO_POWER_MONITORING) |
| 56 power_monitor_( | 56 power_monitor_( |
| 57 params.sample_rate(), | 57 params.sample_rate(), |
| 58 TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)), | 58 TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)), |
| 59 #endif | 59 #endif |
| 60 number_polling_attempts_left_(0) { | 60 wedge_detected_(false) { |
| 61 DCHECK(audio_manager); | 61 DCHECK(audio_manager); |
| 62 DCHECK(handler_); | 62 DCHECK(handler_); |
| 63 DCHECK(sync_reader_); | 63 DCHECK(sync_reader_); |
| 64 DCHECK(message_loop_.get()); | 64 DCHECK(message_loop_.get()); |
| 65 } | 65 } |
| 66 | 66 |
| 67 AudioOutputController::~AudioOutputController() { | 67 AudioOutputController::~AudioOutputController() { |
| 68 DCHECK_EQ(kClosed, state_); | 68 DCHECK_EQ(kClosed, state_); |
| 69 } | 69 } |
| 70 | 70 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 message_loop_->PostTaskAndReply( | 125 message_loop_->PostTaskAndReply( |
| 126 FROM_HERE, | 126 FROM_HERE, |
| 127 base::Bind(&AudioOutputController::DoSwitchOutputDevice, this, | 127 base::Bind(&AudioOutputController::DoSwitchOutputDevice, this, |
| 128 output_device_id), | 128 output_device_id), |
| 129 callback); | 129 callback); |
| 130 } | 130 } |
| 131 | 131 |
| 132 void AudioOutputController::DoCreate(bool is_for_device_change) { | 132 void AudioOutputController::DoCreate(bool is_for_device_change) { |
| 133 DCHECK(message_loop_->BelongsToCurrentThread()); | 133 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 134 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime"); | 134 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime"); |
| 135 TRACE_EVENT0("audio", "AudioOutputController::DoCreate"); | |
| 135 | 136 |
| 136 // Close() can be called before DoCreate() is executed. | 137 // Close() can be called before DoCreate() is executed. |
| 137 if (state_ == kClosed) | 138 if (state_ == kClosed) |
| 138 return; | 139 return; |
| 139 | 140 |
| 140 DoStopCloseAndClearStream(); // Calls RemoveOutputDeviceChangeListener(). | 141 DoStopCloseAndClearStream(); // Calls RemoveOutputDeviceChangeListener(). |
| 141 DCHECK_EQ(kEmpty, state_); | 142 DCHECK_EQ(kEmpty, state_); |
| 142 | 143 |
| 143 stream_ = diverting_to_stream_ ? | 144 stream_ = diverting_to_stream_ ? |
| 144 diverting_to_stream_ : | 145 diverting_to_stream_ : |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 169 state_ = kCreated; | 170 state_ = kCreated; |
| 170 | 171 |
| 171 // And then report we have been created if we haven't done so already. | 172 // And then report we have been created if we haven't done so already. |
| 172 if (!is_for_device_change) | 173 if (!is_for_device_change) |
| 173 handler_->OnCreated(); | 174 handler_->OnCreated(); |
| 174 } | 175 } |
| 175 | 176 |
| 176 void AudioOutputController::DoPlay() { | 177 void AudioOutputController::DoPlay() { |
| 177 DCHECK(message_loop_->BelongsToCurrentThread()); | 178 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 178 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); | 179 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); |
| 180 TRACE_EVENT0("audio", "AudioOutputController::DoPlay"); | |
| 179 | 181 |
| 180 // We can start from created or paused state. | 182 // We can start from created or paused state. |
| 181 if (state_ != kCreated && state_ != kPaused) | 183 if (state_ != kCreated && state_ != kPaused) |
| 182 return; | 184 return; |
| 183 | 185 |
| 184 // Ask for first packet. | 186 // Ask for first packet. |
| 185 sync_reader_->UpdatePendingBytes(0); | 187 sync_reader_->UpdatePendingBytes(0); |
| 186 | 188 |
| 187 state_ = kPlaying; | 189 state_ = kPlaying; |
| 188 | 190 |
| 189 #if defined(AUDIO_POWER_MONITORING) | 191 #if defined(AUDIO_POWER_MONITORING) |
| 190 power_monitor_.Reset(); | 192 power_monitor_.Reset(); |
| 191 power_poll_callback_.Reset( | 193 power_poll_callback_.Reset( |
| 192 base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically, | 194 base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically, |
| 193 this)); | 195 this)); |
| 194 // Run the callback to send an initial notification that we're starting in | 196 // Run the callback to send an initial notification that we're starting in |
| 195 // silence, and to schedule periodic callbacks. | 197 // silence, and to schedule periodic callbacks. |
| 196 power_poll_callback_.callback().Run(); | 198 power_poll_callback_.callback().Run(); |
| 197 #endif | 199 #endif |
| 198 | 200 |
| 199 // We start the AudioOutputStream lazily. | 201 // For UMA tracking purposes, start the wedge detection timer. This allows us |
| 202 // to record statistics about the number of wedged playbacks in the field. | |
| 203 // | |
| 204 // WedgeCheck() will look to see if |wedge_detected_| is still true after the | |
| 205 // timeout expires. Care must be taken to ensure the wedge check delay is | |
| 206 // large enough that the value isn't queried while OnMoreDataIO() is setting | |
| 207 // it. Currently, all buffers are less than 42ms, so anything larger is fine. | |
|
scherkus (not reviewing)
2013/11/04 19:05:12
isn't the important # here how long it takes betwe
DaleCurtis
2013/11/04 21:11:37
Ah, yeah, you're right. The PlayTime UMA values sh
| |
| 208 // | |
| 209 // Timer self-manages its lifetime and WedgeCheck() will only fire if the | |
|
scherkus (not reviewing)
2013/11/04 19:05:12
nit on comment accuracy: WedgeCheck() always runs,
DaleCurtis
2013/11/04 21:11:37
Done.
| |
| 210 // state is still kPlaying. Additional Start() calls will invalidate the | |
| 211 // previous timer. | |
| 212 wedge_timer_.Start( | |
| 213 FROM_HERE, TimeDelta::FromSeconds(1), this, | |
| 214 &AudioOutputController::WedgeCheck); | |
| 215 wedge_detected_ = true; | |
|
scherkus (not reviewing)
2013/11/04 19:05:12
how about on_more_data_io_called_, since that's wh
DaleCurtis
2013/11/04 21:11:37
Done.
| |
| 216 | |
| 200 AllowEntryToOnMoreIOData(); | 217 AllowEntryToOnMoreIOData(); |
| 201 stream_->Start(this); | 218 stream_->Start(this); |
| 202 | 219 |
| 203 handler_->OnPlaying(); | 220 handler_->OnPlaying(); |
| 204 } | 221 } |
| 205 | 222 |
| 206 #if defined(AUDIO_POWER_MONITORING) | 223 #if defined(AUDIO_POWER_MONITORING) |
| 207 void AudioOutputController::ReportPowerMeasurementPeriodically() { | 224 void AudioOutputController::ReportPowerMeasurementPeriodically() { |
| 208 DCHECK(message_loop_->BelongsToCurrentThread()); | 225 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 209 const std::pair<float, bool>& reading = | 226 const std::pair<float, bool>& reading = |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 226 power_poll_callback_.Cancel(); | 243 power_poll_callback_.Cancel(); |
| 227 #endif | 244 #endif |
| 228 | 245 |
| 229 state_ = kPaused; | 246 state_ = kPaused; |
| 230 } | 247 } |
| 231 } | 248 } |
| 232 | 249 |
| 233 void AudioOutputController::DoPause() { | 250 void AudioOutputController::DoPause() { |
| 234 DCHECK(message_loop_->BelongsToCurrentThread()); | 251 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 235 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); | 252 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); |
| 253 TRACE_EVENT0("audio", "AudioOutputController::DoPause"); | |
| 236 | 254 |
| 237 StopStream(); | 255 StopStream(); |
| 238 | 256 |
| 239 if (state_ != kPaused) | 257 if (state_ != kPaused) |
| 240 return; | 258 return; |
| 241 | 259 |
| 242 // Let the renderer know we've stopped. Necessary to let PPAPI clients know | 260 // 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 | 261 // 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(). | 262 // a better way to know when it should exit PPB_Audio_Shared::Run(). |
| 245 sync_reader_->UpdatePendingBytes(-1); | 263 sync_reader_->UpdatePendingBytes(-1); |
| 246 | 264 |
| 247 #if defined(AUDIO_POWER_MONITORING) | 265 #if defined(AUDIO_POWER_MONITORING) |
| 248 // Paused means silence follows. | 266 // Paused means silence follows. |
| 249 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); | 267 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); |
| 250 #endif | 268 #endif |
| 251 | 269 |
| 252 handler_->OnPaused(); | 270 handler_->OnPaused(); |
| 253 } | 271 } |
| 254 | 272 |
| 255 void AudioOutputController::DoClose() { | 273 void AudioOutputController::DoClose() { |
| 256 DCHECK(message_loop_->BelongsToCurrentThread()); | 274 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 257 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); | 275 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); |
| 276 TRACE_EVENT0("audio", "AudioOutputController::DoClose"); | |
| 258 | 277 |
| 259 if (state_ != kClosed) { | 278 if (state_ != kClosed) { |
| 260 DoStopCloseAndClearStream(); | 279 DoStopCloseAndClearStream(); |
| 261 sync_reader_->Close(); | 280 sync_reader_->Close(); |
| 262 state_ = kClosed; | 281 state_ = kClosed; |
| 263 } | 282 } |
| 264 } | 283 } |
| 265 | 284 |
| 266 void AudioOutputController::DoSetVolume(double volume) { | 285 void AudioOutputController::DoSetVolume(double volume) { |
| 267 DCHECK(message_loop_->BelongsToCurrentThread()); | 286 DCHECK(message_loop_->BelongsToCurrentThread()); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 313 AudioBuffersState buffers_state) { | 332 AudioBuffersState buffers_state) { |
| 314 return OnMoreIOData(NULL, dest, buffers_state); | 333 return OnMoreIOData(NULL, dest, buffers_state); |
| 315 } | 334 } |
| 316 | 335 |
| 317 int AudioOutputController::OnMoreIOData(AudioBus* source, | 336 int AudioOutputController::OnMoreIOData(AudioBus* source, |
| 318 AudioBus* dest, | 337 AudioBus* dest, |
| 319 AudioBuffersState buffers_state) { | 338 AudioBuffersState buffers_state) { |
| 320 DisallowEntryToOnMoreIOData(); | 339 DisallowEntryToOnMoreIOData(); |
| 321 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); | 340 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); |
| 322 | 341 |
| 342 // Indicate that we haven't wedged (at least not indefinitely, WedgeCheck() | |
| 343 // may have already fired if OnMoreIOData() took an abnormal amount of time). | |
| 344 if (wedge_detected_) | |
|
scherkus (not reviewing)
2013/11/04 19:05:12
you don't need this if statement
DaleCurtis
2013/11/04 21:11:37
I was trying to avoid setting it if it's already s
| |
| 345 wedge_detected_ = false; | |
|
scherkus (not reviewing)
2013/11/04 19:05:12
IIRC OnMoreIOData() runs on a different thread vs.
DaleCurtis
2013/11/04 21:11:37
Correct, hence the comment above. It seemed overk
| |
| 346 | |
| 323 sync_reader_->Read(source, dest); | 347 sync_reader_->Read(source, dest); |
| 324 | 348 |
| 325 const int frames = dest->frames(); | 349 const int frames = dest->frames(); |
| 326 sync_reader_->UpdatePendingBytes( | 350 sync_reader_->UpdatePendingBytes( |
| 327 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); | 351 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); |
| 328 | 352 |
| 329 #if defined(AUDIO_POWER_MONITORING) | 353 #if defined(AUDIO_POWER_MONITORING) |
| 330 power_monitor_.Scan(*dest, frames); | 354 power_monitor_.Scan(*dest, frames); |
| 331 #endif | 355 #endif |
| 332 | 356 |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 356 diverting_to_stream_ = NULL; | 380 diverting_to_stream_ = NULL; |
| 357 stream_ = NULL; | 381 stream_ = NULL; |
| 358 } | 382 } |
| 359 | 383 |
| 360 state_ = kEmpty; | 384 state_ = kEmpty; |
| 361 } | 385 } |
| 362 | 386 |
| 363 void AudioOutputController::OnDeviceChange() { | 387 void AudioOutputController::OnDeviceChange() { |
| 364 DCHECK(message_loop_->BelongsToCurrentThread()); | 388 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 365 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); | 389 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); |
| 390 TRACE_EVENT0("audio", "AudioOutputController::OnDeviceChange"); | |
| 366 | 391 |
| 367 // TODO(dalecurtis): Notify the renderer side that a device change has | 392 // TODO(dalecurtis): Notify the renderer side that a device change has |
| 368 // occurred. Currently querying the hardware information here will lead to | 393 // occurred. Currently querying the hardware information here will lead to |
| 369 // crashes on OSX. See http://crbug.com/158170. | 394 // crashes on OSX. See http://crbug.com/158170. |
| 370 | 395 |
| 371 // Recreate the stream (DoCreate() will first shut down an existing stream). | 396 // Recreate the stream (DoCreate() will first shut down an existing stream). |
| 372 // Exit if we ran into an error. | 397 // Exit if we ran into an error. |
| 373 const State original_state = state_; | 398 const State original_state = state_; |
| 374 DoCreate(true); | 399 DoCreate(true); |
| 375 if (!stream_ || state_ == kError) | 400 if (!stream_ || state_ == kError) |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 434 void AudioOutputController::AllowEntryToOnMoreIOData() { | 459 void AudioOutputController::AllowEntryToOnMoreIOData() { |
| 435 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); | 460 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); |
| 436 base::AtomicRefCountInc(&num_allowed_io_); | 461 base::AtomicRefCountInc(&num_allowed_io_); |
| 437 } | 462 } |
| 438 | 463 |
| 439 void AudioOutputController::DisallowEntryToOnMoreIOData() { | 464 void AudioOutputController::DisallowEntryToOnMoreIOData() { |
| 440 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); | 465 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); |
| 441 DCHECK(is_zero); | 466 DCHECK(is_zero); |
| 442 } | 467 } |
| 443 | 468 |
| 469 void AudioOutputController::WedgeCheck() { | |
| 470 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 471 | |
| 472 // If we should be playing and we haven't, that's a wedge. | |
| 473 if (state_ == kPlaying) { | |
| 474 UMA_HISTOGRAM_BOOLEAN( | |
| 475 "Media.AudioOutputControllerWedgeDetected", wedge_detected_); | |
|
jar (doing other things)
2013/11/04 07:03:43
This seems to use a histogram as a counter. With
DaleCurtis
2013/11/04 18:57:32
I'm confused with what you mean by == and !=. |we
| |
| 476 } | |
| 477 } | |
| 478 | |
| 444 } // namespace media | 479 } // namespace media |
| OLD | NEW |