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 on_more_io_data_called_(0) { |
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 |on_more_io_data_called_| is true after |
| 205 // the 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. |
| 208 // |
| 209 // Timer self-manages its lifetime and WedgeCheck() will only record the UMA |
| 210 // statistic if state is still kPlaying. Additional Start() calls will |
| 211 // invalidate the previous timer. |
| 212 wedge_timer_.reset(new base::OneShotTimer<AudioOutputController>()); |
| 213 wedge_timer_->Start( |
| 214 FROM_HERE, TimeDelta::FromSeconds(3), this, |
| 215 &AudioOutputController::WedgeCheck); |
| 216 on_more_io_data_called_ = 0; |
| 217 |
200 AllowEntryToOnMoreIOData(); | 218 AllowEntryToOnMoreIOData(); |
201 stream_->Start(this); | 219 stream_->Start(this); |
202 | 220 |
203 handler_->OnPlaying(); | 221 handler_->OnPlaying(); |
204 } | 222 } |
205 | 223 |
206 #if defined(AUDIO_POWER_MONITORING) | 224 #if defined(AUDIO_POWER_MONITORING) |
207 void AudioOutputController::ReportPowerMeasurementPeriodically() { | 225 void AudioOutputController::ReportPowerMeasurementPeriodically() { |
208 DCHECK(message_loop_->BelongsToCurrentThread()); | 226 DCHECK(message_loop_->BelongsToCurrentThread()); |
209 const std::pair<float, bool>& reading = | 227 const std::pair<float, bool>& reading = |
210 power_monitor_.ReadCurrentPowerAndClip(); | 228 power_monitor_.ReadCurrentPowerAndClip(); |
211 handler_->OnPowerMeasured(reading.first, reading.second); | 229 handler_->OnPowerMeasured(reading.first, reading.second); |
212 message_loop_->PostDelayedTask( | 230 message_loop_->PostDelayedTask( |
213 FROM_HERE, power_poll_callback_.callback(), | 231 FROM_HERE, power_poll_callback_.callback(), |
214 TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond); | 232 TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond); |
215 } | 233 } |
216 #endif | 234 #endif |
217 | 235 |
218 void AudioOutputController::StopStream() { | 236 void AudioOutputController::StopStream() { |
219 DCHECK(message_loop_->BelongsToCurrentThread()); | 237 DCHECK(message_loop_->BelongsToCurrentThread()); |
220 | 238 |
221 if (state_ == kPlaying) { | 239 if (state_ == kPlaying) { |
| 240 wedge_timer_.reset(); |
222 stream_->Stop(); | 241 stream_->Stop(); |
223 DisallowEntryToOnMoreIOData(); | 242 DisallowEntryToOnMoreIOData(); |
224 | 243 |
225 #if defined(AUDIO_POWER_MONITORING) | 244 #if defined(AUDIO_POWER_MONITORING) |
226 power_poll_callback_.Cancel(); | 245 power_poll_callback_.Cancel(); |
227 #endif | 246 #endif |
228 | 247 |
229 state_ = kPaused; | 248 state_ = kPaused; |
230 } | 249 } |
231 } | 250 } |
232 | 251 |
233 void AudioOutputController::DoPause() { | 252 void AudioOutputController::DoPause() { |
234 DCHECK(message_loop_->BelongsToCurrentThread()); | 253 DCHECK(message_loop_->BelongsToCurrentThread()); |
235 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); | 254 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime"); |
| 255 TRACE_EVENT0("audio", "AudioOutputController::DoPause"); |
236 | 256 |
237 StopStream(); | 257 StopStream(); |
238 | 258 |
239 if (state_ != kPaused) | 259 if (state_ != kPaused) |
240 return; | 260 return; |
241 | 261 |
242 // Let the renderer know we've stopped. Necessary to let PPAPI clients know | 262 // 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 | 263 // 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(). | 264 // a better way to know when it should exit PPB_Audio_Shared::Run(). |
245 sync_reader_->UpdatePendingBytes(-1); | 265 sync_reader_->UpdatePendingBytes(-1); |
246 | 266 |
247 #if defined(AUDIO_POWER_MONITORING) | 267 #if defined(AUDIO_POWER_MONITORING) |
248 // Paused means silence follows. | 268 // Paused means silence follows. |
249 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); | 269 handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false); |
250 #endif | 270 #endif |
251 | 271 |
252 handler_->OnPaused(); | 272 handler_->OnPaused(); |
253 } | 273 } |
254 | 274 |
255 void AudioOutputController::DoClose() { | 275 void AudioOutputController::DoClose() { |
256 DCHECK(message_loop_->BelongsToCurrentThread()); | 276 DCHECK(message_loop_->BelongsToCurrentThread()); |
257 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); | 277 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime"); |
| 278 TRACE_EVENT0("audio", "AudioOutputController::DoClose"); |
258 | 279 |
259 if (state_ != kClosed) { | 280 if (state_ != kClosed) { |
260 DoStopCloseAndClearStream(); | 281 DoStopCloseAndClearStream(); |
261 sync_reader_->Close(); | 282 sync_reader_->Close(); |
262 state_ = kClosed; | 283 state_ = kClosed; |
263 } | 284 } |
264 } | 285 } |
265 | 286 |
266 void AudioOutputController::DoSetVolume(double volume) { | 287 void AudioOutputController::DoSetVolume(double volume) { |
267 DCHECK(message_loop_->BelongsToCurrentThread()); | 288 DCHECK(message_loop_->BelongsToCurrentThread()); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 AudioBuffersState buffers_state) { | 334 AudioBuffersState buffers_state) { |
314 return OnMoreIOData(NULL, dest, buffers_state); | 335 return OnMoreIOData(NULL, dest, buffers_state); |
315 } | 336 } |
316 | 337 |
317 int AudioOutputController::OnMoreIOData(AudioBus* source, | 338 int AudioOutputController::OnMoreIOData(AudioBus* source, |
318 AudioBus* dest, | 339 AudioBus* dest, |
319 AudioBuffersState buffers_state) { | 340 AudioBuffersState buffers_state) { |
320 DisallowEntryToOnMoreIOData(); | 341 DisallowEntryToOnMoreIOData(); |
321 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); | 342 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); |
322 | 343 |
| 344 // Indicate that we haven't wedged (at least not indefinitely, WedgeCheck() |
| 345 // may have already fired if OnMoreIOData() took an abnormal amount of time). |
| 346 // Since this thread is the only writer of |on_more_io_data_called_| once the |
| 347 // thread starts, its safe to compare and then increment. |
| 348 if (base::AtomicRefCountIsZero(&on_more_io_data_called_)) |
| 349 base::AtomicRefCountInc(&on_more_io_data_called_); |
| 350 |
323 sync_reader_->Read(source, dest); | 351 sync_reader_->Read(source, dest); |
324 | 352 |
325 const int frames = dest->frames(); | 353 const int frames = dest->frames(); |
326 sync_reader_->UpdatePendingBytes( | 354 sync_reader_->UpdatePendingBytes( |
327 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); | 355 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); |
328 | 356 |
329 #if defined(AUDIO_POWER_MONITORING) | 357 #if defined(AUDIO_POWER_MONITORING) |
330 power_monitor_.Scan(*dest, frames); | 358 power_monitor_.Scan(*dest, frames); |
331 #endif | 359 #endif |
332 | 360 |
(...skipping 23 matching lines...) Expand all Loading... |
356 diverting_to_stream_ = NULL; | 384 diverting_to_stream_ = NULL; |
357 stream_ = NULL; | 385 stream_ = NULL; |
358 } | 386 } |
359 | 387 |
360 state_ = kEmpty; | 388 state_ = kEmpty; |
361 } | 389 } |
362 | 390 |
363 void AudioOutputController::OnDeviceChange() { | 391 void AudioOutputController::OnDeviceChange() { |
364 DCHECK(message_loop_->BelongsToCurrentThread()); | 392 DCHECK(message_loop_->BelongsToCurrentThread()); |
365 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); | 393 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime"); |
| 394 TRACE_EVENT0("audio", "AudioOutputController::OnDeviceChange"); |
366 | 395 |
367 // TODO(dalecurtis): Notify the renderer side that a device change has | 396 // TODO(dalecurtis): Notify the renderer side that a device change has |
368 // occurred. Currently querying the hardware information here will lead to | 397 // occurred. Currently querying the hardware information here will lead to |
369 // crashes on OSX. See http://crbug.com/158170. | 398 // crashes on OSX. See http://crbug.com/158170. |
370 | 399 |
371 // Recreate the stream (DoCreate() will first shut down an existing stream). | 400 // Recreate the stream (DoCreate() will first shut down an existing stream). |
372 // Exit if we ran into an error. | 401 // Exit if we ran into an error. |
373 const State original_state = state_; | 402 const State original_state = state_; |
374 DoCreate(true); | 403 DoCreate(true); |
375 if (!stream_ || state_ == kError) | 404 if (!stream_ || state_ == kError) |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 void AudioOutputController::AllowEntryToOnMoreIOData() { | 463 void AudioOutputController::AllowEntryToOnMoreIOData() { |
435 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); | 464 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); |
436 base::AtomicRefCountInc(&num_allowed_io_); | 465 base::AtomicRefCountInc(&num_allowed_io_); |
437 } | 466 } |
438 | 467 |
439 void AudioOutputController::DisallowEntryToOnMoreIOData() { | 468 void AudioOutputController::DisallowEntryToOnMoreIOData() { |
440 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); | 469 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); |
441 DCHECK(is_zero); | 470 DCHECK(is_zero); |
442 } | 471 } |
443 | 472 |
| 473 void AudioOutputController::WedgeCheck() { |
| 474 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 475 |
| 476 // If we should be playing and we haven't, that's a wedge. |
| 477 if (state_ == kPlaying) { |
| 478 UMA_HISTOGRAM_BOOLEAN("Media.AudioOutputControllerPlaybackStartupSuccess", |
| 479 base::AtomicRefCountIsOne(&on_more_io_data_called_)); |
| 480 } |
| 481 } |
| 482 |
444 } // namespace media | 483 } // namespace media |
OLD | NEW |