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.h" | 9 #include "base/message_loop.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 : audio_manager_(audio_manager), | 43 : audio_manager_(audio_manager), |
44 params_(params), | 44 params_(params), |
45 handler_(handler), | 45 handler_(handler), |
46 stream_(NULL), | 46 stream_(NULL), |
47 diverting_to_stream_(NULL), | 47 diverting_to_stream_(NULL), |
48 volume_(1.0), | 48 volume_(1.0), |
49 state_(kEmpty), | 49 state_(kEmpty), |
50 num_allowed_io_(0), | 50 num_allowed_io_(0), |
51 sync_reader_(sync_reader), | 51 sync_reader_(sync_reader), |
52 message_loop_(audio_manager->GetMessageLoop()), | 52 message_loop_(audio_manager->GetMessageLoop()), |
53 number_polling_attempts_left_(0), | 53 number_polling_attempts_left_(0) { |
54 weak_this_(this) { | |
55 DCHECK(audio_manager); | 54 DCHECK(audio_manager); |
56 DCHECK(handler_); | 55 DCHECK(handler_); |
57 DCHECK(sync_reader_); | 56 DCHECK(sync_reader_); |
58 DCHECK(message_loop_); | 57 DCHECK(message_loop_); |
59 } | 58 } |
60 | 59 |
61 AudioOutputController::~AudioOutputController() { | 60 AudioOutputController::~AudioOutputController() { |
62 DCHECK_EQ(kClosed, state_); | 61 DCHECK_EQ(kClosed, state_); |
63 } | 62 } |
64 | 63 |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
139 // Finally set the state to kCreated. | 138 // Finally set the state to kCreated. |
140 state_ = kCreated; | 139 state_ = kCreated; |
141 | 140 |
142 // And then report we have been created if we haven't done so already. | 141 // And then report we have been created if we haven't done so already. |
143 if (!is_for_device_change) | 142 if (!is_for_device_change) |
144 handler_->OnCreated(); | 143 handler_->OnCreated(); |
145 } | 144 } |
146 | 145 |
147 void AudioOutputController::DoPlay() { | 146 void AudioOutputController::DoPlay() { |
148 DCHECK(message_loop_->BelongsToCurrentThread()); | 147 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 148 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); |
149 | 149 |
150 // We can start from created or paused state. | 150 // We can start from created or paused state. |
151 if (state_ != kCreated && state_ != kPaused) | 151 if (state_ != kCreated && state_ != kPaused) |
152 return; | 152 return; |
153 | 153 |
154 state_ = kStarting; | |
155 | |
156 // Ask for first packet. | 154 // Ask for first packet. |
157 sync_reader_->UpdatePendingBytes(0); | 155 sync_reader_->UpdatePendingBytes(0); |
158 | 156 |
159 // Cannot start stream immediately, should give renderer some time | |
160 // to deliver data. | |
161 // TODO(vrk): The polling here and in WaitTillDataReady() is pretty clunky. | |
162 // Refine the API such that polling is no longer needed. (crbug.com/112196) | |
163 number_polling_attempts_left_ = kPollNumAttempts; | |
164 DCHECK(!weak_this_.HasWeakPtrs()); | |
165 message_loop_->PostDelayedTask( | |
166 FROM_HERE, | |
167 base::Bind(&AudioOutputController::PollAndStartIfDataReady, | |
168 weak_this_.GetWeakPtr()), | |
169 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | |
170 } | |
171 | |
172 void AudioOutputController::PollAndStartIfDataReady() { | |
173 DCHECK(message_loop_->BelongsToCurrentThread()); | |
174 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime"); | |
175 | |
176 DCHECK_EQ(kStarting, state_); | |
177 | |
178 // If we are ready to start the stream, start it. | |
179 if (--number_polling_attempts_left_ == 0 || | |
180 sync_reader_->DataReady()) { | |
181 StartStream(); | |
182 } else { | |
183 message_loop_->PostDelayedTask( | |
184 FROM_HERE, | |
185 base::Bind(&AudioOutputController::PollAndStartIfDataReady, | |
186 weak_this_.GetWeakPtr()), | |
187 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | |
188 } | |
189 } | |
190 | |
191 void AudioOutputController::StartStream() { | |
192 DCHECK(message_loop_->BelongsToCurrentThread()); | |
193 state_ = kPlaying; | 157 state_ = kPlaying; |
194 | |
195 silence_detector_.reset(new AudioSilenceDetector( | 158 silence_detector_.reset(new AudioSilenceDetector( |
196 params_.sample_rate(), | 159 params_.sample_rate(), |
197 TimeDelta::FromMilliseconds(kQuestionableSilencePeriodMillis), | 160 TimeDelta::FromMilliseconds(kQuestionableSilencePeriodMillis), |
198 kIndistinguishableSilenceThreshold)); | 161 kIndistinguishableSilenceThreshold)); |
199 | 162 |
200 // We start the AudioOutputStream lazily. | 163 // We start the AudioOutputStream lazily. |
201 AllowEntryToOnMoreIOData(); | 164 AllowEntryToOnMoreIOData(); |
202 stream_->Start(this); | 165 stream_->Start(this); |
203 | 166 |
204 // Tell the event handler that we are now playing, and also start the silence | 167 // Tell the event handler that we are now playing, and also start the silence |
205 // detection notifications. | 168 // detection notifications. |
206 handler_->OnPlaying(); | 169 handler_->OnPlaying(); |
207 silence_detector_->Start( | 170 silence_detector_->Start( |
208 base::Bind(&EventHandler::OnAudible, base::Unretained(handler_))); | 171 base::Bind(&EventHandler::OnAudible, base::Unretained(handler_))); |
209 } | 172 } |
210 | 173 |
211 void AudioOutputController::StopStream() { | 174 void AudioOutputController::StopStream() { |
212 DCHECK(message_loop_->BelongsToCurrentThread()); | 175 DCHECK(message_loop_->BelongsToCurrentThread()); |
213 | 176 |
214 if (state_ == kStarting) { | 177 if (state_ == kPlaying) { |
215 // Cancel in-progress polling start. | |
216 weak_this_.InvalidateWeakPtrs(); | |
217 state_ = kPaused; | |
218 } else if (state_ == kPlaying) { | |
219 stream_->Stop(); | 178 stream_->Stop(); |
220 DisallowEntryToOnMoreIOData(); | 179 DisallowEntryToOnMoreIOData(); |
221 silence_detector_->Stop(true); | 180 silence_detector_->Stop(true); |
222 silence_detector_.reset(); | 181 silence_detector_.reset(); |
223 state_ = kPaused; | 182 state_ = kPaused; |
224 } | 183 } |
225 } | 184 } |
226 | 185 |
227 void AudioOutputController::DoPause() { | 186 void AudioOutputController::DoPause() { |
228 DCHECK(message_loop_->BelongsToCurrentThread()); | 187 DCHECK(message_loop_->BelongsToCurrentThread()); |
(...skipping 23 matching lines...) Expand all Loading... |
252 | 211 |
253 void AudioOutputController::DoSetVolume(double volume) { | 212 void AudioOutputController::DoSetVolume(double volume) { |
254 DCHECK(message_loop_->BelongsToCurrentThread()); | 213 DCHECK(message_loop_->BelongsToCurrentThread()); |
255 | 214 |
256 // Saves the volume to a member first. We may not be able to set the volume | 215 // Saves the volume to a member first. We may not be able to set the volume |
257 // right away but when the stream is created we'll set the volume. | 216 // right away but when the stream is created we'll set the volume. |
258 volume_ = volume; | 217 volume_ = volume; |
259 | 218 |
260 switch (state_) { | 219 switch (state_) { |
261 case kCreated: | 220 case kCreated: |
262 case kStarting: | |
263 case kPlaying: | 221 case kPlaying: |
264 case kPaused: | 222 case kPaused: |
265 stream_->SetVolume(volume_); | 223 stream_->SetVolume(volume_); |
266 break; | 224 break; |
267 default: | 225 default: |
268 return; | 226 return; |
269 } | 227 } |
270 } | 228 } |
271 | 229 |
272 void AudioOutputController::DoReportError() { | 230 void AudioOutputController::DoReportError() { |
273 DCHECK(message_loop_->BelongsToCurrentThread()); | 231 DCHECK(message_loop_->BelongsToCurrentThread()); |
274 if (state_ != kClosed) | 232 if (state_ != kClosed) |
275 handler_->OnError(); | 233 handler_->OnError(); |
276 } | 234 } |
277 | 235 |
278 int AudioOutputController::OnMoreData(AudioBus* dest, | 236 int AudioOutputController::OnMoreData(AudioBus* dest, |
279 AudioBuffersState buffers_state) { | 237 AudioBuffersState buffers_state) { |
280 return OnMoreIOData(NULL, dest, buffers_state); | 238 return OnMoreIOData(NULL, dest, buffers_state); |
281 } | 239 } |
282 | 240 |
283 int AudioOutputController::OnMoreIOData(AudioBus* source, | 241 int AudioOutputController::OnMoreIOData(AudioBus* source, |
284 AudioBus* dest, | 242 AudioBus* dest, |
285 AudioBuffersState buffers_state) { | 243 AudioBuffersState buffers_state) { |
286 DisallowEntryToOnMoreIOData(); | 244 DisallowEntryToOnMoreIOData(); |
287 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); | 245 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); |
288 | 246 |
289 // The OS level audio APIs on Linux and Windows all have problems requesting | 247 // The OS level audio APIs on Linux and Windows all have problems requesting |
290 // data on a fixed interval. Sometimes they will issue calls back to back | 248 // data on a fixed interval. Sometimes they will issue calls back to back |
291 // which can cause glitching, so wait until the renderer is ready for Read(). | 249 // which can cause glitching, so wait until the renderer is ready. |
| 250 // |
| 251 // We also need to wait when diverting since the virtual stream will call this |
| 252 // multiple times without waiting. |
| 253 // |
| 254 // NEVER wait on OSX unless a virtual stream is connected, otherwise we can |
| 255 // end up hanging the entire OS. |
292 // | 256 // |
293 // See many bugs for context behind this decision: http://crbug.com/170498, | 257 // See many bugs for context behind this decision: http://crbug.com/170498, |
294 // http://crbug.com/171651, http://crbug.com/174985, and more. | 258 // http://crbug.com/171651, http://crbug.com/174985, and more. |
295 #if defined(OS_WIN) || defined(OS_LINUX) | 259 #if defined(OS_WIN) || defined(OS_LINUX) |
296 WaitTillDataReady(); | 260 const bool kShouldBlock = true; |
| 261 #else |
| 262 const bool kShouldBlock = diverting_to_stream_ != NULL; |
297 #endif | 263 #endif |
298 | 264 |
299 const int frames = sync_reader_->Read(source, dest); | 265 const int frames = sync_reader_->Read(kShouldBlock, source, dest); |
300 DCHECK_LE(0, frames); | 266 DCHECK_LE(0, frames); |
301 sync_reader_->UpdatePendingBytes( | 267 sync_reader_->UpdatePendingBytes( |
302 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); | 268 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); |
303 | 269 |
304 silence_detector_->Scan(dest, frames); | 270 silence_detector_->Scan(dest, frames); |
305 | 271 |
306 AllowEntryToOnMoreIOData(); | 272 AllowEntryToOnMoreIOData(); |
307 return frames; | 273 return frames; |
308 } | 274 } |
309 | 275 |
310 void AudioOutputController::WaitTillDataReady() { | |
311 // Most of the time the data is ready already. | |
312 if (sync_reader_->DataReady()) | |
313 return; | |
314 | |
315 base::TimeTicks start = base::TimeTicks::Now(); | |
316 const base::TimeDelta kMaxWait = base::TimeDelta::FromMilliseconds(20); | |
317 #if defined(OS_WIN) | |
318 // Sleep(0) on windows lets the other threads run. | |
319 const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(0); | |
320 #else | |
321 // We want to sleep for a bit here, as otherwise a backgrounded renderer won't | |
322 // get enough cpu to send the data and the high priority thread in the browser | |
323 // will use up a core causing even more skips. | |
324 const base::TimeDelta kSleep = base::TimeDelta::FromMilliseconds(2); | |
325 #endif | |
326 base::TimeDelta time_since_start; | |
327 do { | |
328 base::PlatformThread::Sleep(kSleep); | |
329 time_since_start = base::TimeTicks::Now() - start; | |
330 } while (!sync_reader_->DataReady() && (time_since_start < kMaxWait)); | |
331 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady", | |
332 time_since_start, | |
333 base::TimeDelta::FromMilliseconds(1), | |
334 base::TimeDelta::FromMilliseconds(1000), | |
335 50); | |
336 } | |
337 | |
338 void AudioOutputController::OnError(AudioOutputStream* stream) { | 276 void AudioOutputController::OnError(AudioOutputStream* stream) { |
339 // Handle error on the audio controller thread. | 277 // Handle error on the audio controller thread. |
340 message_loop_->PostTask(FROM_HERE, base::Bind( | 278 message_loop_->PostTask(FROM_HERE, base::Bind( |
341 &AudioOutputController::DoReportError, this)); | 279 &AudioOutputController::DoReportError, this)); |
342 } | 280 } |
343 | 281 |
344 void AudioOutputController::DoStopCloseAndClearStream() { | 282 void AudioOutputController::DoStopCloseAndClearStream() { |
345 DCHECK(message_loop_->BelongsToCurrentThread()); | 283 DCHECK(message_loop_->BelongsToCurrentThread()); |
346 | 284 |
347 // Allow calling unconditionally and bail if we don't have a stream_ to close. | 285 // Allow calling unconditionally and bail if we don't have a stream_ to close. |
(...skipping 23 matching lines...) Expand all Loading... |
371 | 309 |
372 // Recreate the stream (DoCreate() will first shut down an existing stream). | 310 // Recreate the stream (DoCreate() will first shut down an existing stream). |
373 // Exit if we ran into an error. | 311 // Exit if we ran into an error. |
374 const State original_state = state_; | 312 const State original_state = state_; |
375 DoCreate(true); | 313 DoCreate(true); |
376 if (!stream_ || state_ == kError) | 314 if (!stream_ || state_ == kError) |
377 return; | 315 return; |
378 | 316 |
379 // Get us back to the original state or an equivalent state. | 317 // Get us back to the original state or an equivalent state. |
380 switch (original_state) { | 318 switch (original_state) { |
381 case kStarting: | |
382 case kPlaying: | 319 case kPlaying: |
383 DoPlay(); | 320 DoPlay(); |
384 return; | 321 return; |
385 case kCreated: | 322 case kCreated: |
386 case kPaused: | 323 case kPaused: |
387 // From the outside these two states are equivalent. | 324 // From the outside these two states are equivalent. |
388 return; | 325 return; |
389 default: | 326 default: |
390 NOTREACHED() << "Invalid original state."; | 327 NOTREACHED() << "Invalid original state."; |
391 } | 328 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
437 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); | 374 DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_)); |
438 base::AtomicRefCountInc(&num_allowed_io_); | 375 base::AtomicRefCountInc(&num_allowed_io_); |
439 } | 376 } |
440 | 377 |
441 void AudioOutputController::DisallowEntryToOnMoreIOData() { | 378 void AudioOutputController::DisallowEntryToOnMoreIOData() { |
442 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); | 379 const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_); |
443 DCHECK(is_zero); | 380 DCHECK(is_zero); |
444 } | 381 } |
445 | 382 |
446 } // namespace media | 383 } // namespace media |
OLD | NEW |