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 |