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/synchronization/waitable_event.h" | 10 #include "base/synchronization/waitable_event.h" |
(...skipping 11 matching lines...) Expand all Loading... | |
22 | 22 |
23 // Polling-related constants. | 23 // Polling-related constants. |
24 const int AudioOutputController::kPollNumAttempts = 3; | 24 const int AudioOutputController::kPollNumAttempts = 3; |
25 const int AudioOutputController::kPollPauseInMilliseconds = 3; | 25 const int AudioOutputController::kPollPauseInMilliseconds = 3; |
26 | 26 |
27 AudioOutputController::AudioOutputController(AudioManager* audio_manager, | 27 AudioOutputController::AudioOutputController(AudioManager* audio_manager, |
28 EventHandler* handler, | 28 EventHandler* handler, |
29 const AudioParameters& params, | 29 const AudioParameters& params, |
30 SyncReader* sync_reader) | 30 SyncReader* sync_reader) |
31 : audio_manager_(audio_manager), | 31 : audio_manager_(audio_manager), |
32 params_(params), | |
32 handler_(handler), | 33 handler_(handler), |
33 stream_(NULL), | 34 stream_(NULL), |
35 stream_glue_(new Glue(this, sync_reader)), | |
34 volume_(1.0), | 36 volume_(1.0), |
35 state_(kEmpty), | 37 state_(kEmpty), |
36 sync_reader_(sync_reader), | 38 sync_reader_(sync_reader), |
37 message_loop_(audio_manager->GetMessageLoop()), | 39 message_loop_(audio_manager->GetMessageLoop()), |
38 number_polling_attempts_left_(0), | 40 number_polling_attempts_left_(0), |
39 params_(params), | |
40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { | 41 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { |
41 } | 42 } |
42 | 43 |
43 AudioOutputController::~AudioOutputController() { | 44 AudioOutputController::~AudioOutputController() { |
44 DCHECK_EQ(kClosed, state_); | 45 DCHECK_EQ(kClosed, state_); |
45 | 46 |
46 if (message_loop_->BelongsToCurrentThread()) { | 47 if (message_loop_->BelongsToCurrentThread()) { |
47 DoStopCloseAndClearStream(NULL); | 48 DoStopCloseAndClearStream(NULL); |
48 } else { | 49 } else { |
49 // http://crbug.com/120973 | 50 // http://crbug.com/120973 |
(...skipping 13 matching lines...) Expand all Loading... | |
63 AudioManager* audio_manager, | 64 AudioManager* audio_manager, |
64 EventHandler* event_handler, | 65 EventHandler* event_handler, |
65 const AudioParameters& params, | 66 const AudioParameters& params, |
66 SyncReader* sync_reader) { | 67 SyncReader* sync_reader) { |
67 DCHECK(audio_manager); | 68 DCHECK(audio_manager); |
68 DCHECK(sync_reader); | 69 DCHECK(sync_reader); |
69 | 70 |
70 if (!params.IsValid() || !audio_manager) | 71 if (!params.IsValid() || !audio_manager) |
71 return NULL; | 72 return NULL; |
72 | 73 |
73 // Starts the audio controller thread. | |
74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( | 74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( |
75 audio_manager, event_handler, params, sync_reader)); | 75 audio_manager, event_handler, params, sync_reader)); |
76 | |
77 controller->message_loop_->PostTask(FROM_HERE, base::Bind( | 76 controller->message_loop_->PostTask(FROM_HERE, base::Bind( |
78 &AudioOutputController::DoCreate, controller)); | 77 &AudioOutputController::DoCreate, controller)); |
79 | |
80 return controller; | 78 return controller; |
81 } | 79 } |
82 | 80 |
83 void AudioOutputController::Play() { | 81 void AudioOutputController::Play() { |
84 DCHECK(message_loop_); | 82 DCHECK(message_loop_); |
85 message_loop_->PostTask(FROM_HERE, base::Bind( | 83 message_loop_->PostTask(FROM_HERE, base::Bind( |
86 &AudioOutputController::DoPlay, this)); | 84 &AudioOutputController::DoPlay, this)); |
87 } | 85 } |
88 | 86 |
89 void AudioOutputController::Pause() { | 87 void AudioOutputController::Pause() { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
131 | 129 |
132 if (!stream_->Open()) { | 130 if (!stream_->Open()) { |
133 state_ = kError; | 131 state_ = kError; |
134 DoStopCloseAndClearStream(NULL); | 132 DoStopCloseAndClearStream(NULL); |
135 | 133 |
136 // TODO(hclam): Define error types. | 134 // TODO(hclam): Define error types. |
137 handler_->OnError(this, 0); | 135 handler_->OnError(this, 0); |
138 return; | 136 return; |
139 } | 137 } |
140 | 138 |
141 // Everything started okay, so register for state change callbacks if we have | 139 // Everything started okay, so re-register for state change callbacks. Note: |
142 // not already done so. | 140 // The call to DoStopCloseAndClearStream() above called |
143 if (state_ != kRecreating) | 141 // RemoveOutputDeviceChangeListener(). |
144 audio_manager_->AddOutputDeviceChangeListener(this); | 142 audio_manager_->AddOutputDeviceChangeListener(this); |
145 | 143 |
146 // We have successfully opened the stream. Set the initial volume. | 144 // We have successfully opened the stream. Set the initial volume. |
147 stream_->SetVolume(volume_); | 145 stream_->SetVolume(volume_); |
148 | 146 |
149 // Finally set the state to kCreated. | 147 // Finally set the state to kCreated. |
150 State original_state = state_; | 148 State original_state = state_; |
151 state_ = kCreated; | 149 state_ = kCreated; |
152 | 150 |
153 // And then report we have been created if we haven't done so already. | 151 // And then report we have been created if we haven't done so already. |
154 if (original_state != kRecreating) | 152 if (original_state != kRecreating) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
208 weak_this_.GetWeakPtr()), | 206 weak_this_.GetWeakPtr()), |
209 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | 207 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); |
210 } | 208 } |
211 } | 209 } |
212 | 210 |
213 void AudioOutputController::StartStream() { | 211 void AudioOutputController::StartStream() { |
214 DCHECK(message_loop_->BelongsToCurrentThread()); | 212 DCHECK(message_loop_->BelongsToCurrentThread()); |
215 state_ = kPlaying; | 213 state_ = kPlaying; |
216 | 214 |
217 // We start the AudioOutputStream lazily. | 215 // We start the AudioOutputStream lazily. |
218 stream_->Start(this); | 216 stream_->Start(stream_glue_); |
219 | 217 |
220 // Tell the event handler that we are now playing. | 218 // Tell the event handler that we are now playing. |
221 handler_->OnPlaying(this); | 219 handler_->OnPlaying(this); |
222 } | 220 } |
223 | 221 |
224 void AudioOutputController::DoPause() { | 222 void AudioOutputController::DoPause() { |
225 DCHECK(message_loop_->BelongsToCurrentThread()); | 223 DCHECK(message_loop_->BelongsToCurrentThread()); |
226 | 224 |
227 if (stream_) { | 225 if (stream_) { |
228 // Then we stop the audio device. This is not the perfect solution | 226 // Then we stop the audio device. This is not the perfect solution |
(...skipping 26 matching lines...) Expand all Loading... | |
255 void AudioOutputController::DoFlush() { | 253 void AudioOutputController::DoFlush() { |
256 DCHECK(message_loop_->BelongsToCurrentThread()); | 254 DCHECK(message_loop_->BelongsToCurrentThread()); |
257 | 255 |
258 // TODO(hclam): Actually flush the audio device. | 256 // TODO(hclam): Actually flush the audio device. |
259 } | 257 } |
260 | 258 |
261 void AudioOutputController::DoClose() { | 259 void AudioOutputController::DoClose() { |
262 DCHECK(message_loop_->BelongsToCurrentThread()); | 260 DCHECK(message_loop_->BelongsToCurrentThread()); |
263 | 261 |
264 if (state_ != kClosed) { | 262 if (state_ != kClosed) { |
263 DCHECK(!divert_glue_); | |
265 DoStopCloseAndClearStream(NULL); | 264 DoStopCloseAndClearStream(NULL); |
266 sync_reader_->Close(); | 265 sync_reader_->Close(); |
267 state_ = kClosed; | 266 state_ = kClosed; |
268 } | 267 } |
269 } | 268 } |
270 | 269 |
271 void AudioOutputController::DoSetVolume(double volume) { | 270 void AudioOutputController::DoSetVolume(double volume) { |
272 DCHECK(message_loop_->BelongsToCurrentThread()); | 271 DCHECK(message_loop_->BelongsToCurrentThread()); |
273 | 272 |
274 // Saves the volume to a member first. We may not be able to set the volume | 273 // Saves the volume to a member first. We may not be able to set the volume |
(...skipping 12 matching lines...) Expand all Loading... | |
287 return; | 286 return; |
288 } | 287 } |
289 } | 288 } |
290 | 289 |
291 void AudioOutputController::DoReportError(int code) { | 290 void AudioOutputController::DoReportError(int code) { |
292 DCHECK(message_loop_->BelongsToCurrentThread()); | 291 DCHECK(message_loop_->BelongsToCurrentThread()); |
293 if (state_ != kClosed) | 292 if (state_ != kClosed) |
294 handler_->OnError(this, code); | 293 handler_->OnError(this, code); |
295 } | 294 } |
296 | 295 |
297 int AudioOutputController::OnMoreData(AudioBus* dest, | 296 AudioOutputController::Glue::Glue(AudioOutputController* controller, |
298 AudioBuffersState buffers_state) { | 297 SyncReader* source) |
299 return OnMoreIOData(NULL, dest, buffers_state); | 298 : controller_(controller), |
299 source_(source), | |
300 is_reading_from_source_(false) { | |
300 } | 301 } |
301 | 302 |
302 int AudioOutputController::OnMoreIOData(AudioBus* source, | 303 AudioOutputController::Glue::~Glue() { |
303 AudioBus* dest, | 304 } |
304 AudioBuffersState buffers_state) { | 305 |
306 void AudioOutputController::Glue::PassSoon(const scoped_refptr<Glue>& to) { | |
DaleCurtis
2012/12/12 01:29:15
This is all really complicated and I don't quite f
miu
2012/12/12 03:13:39
Main reason: Per our hallway conversation, "Though
DaleCurtis
2012/12/12 20:38:38
How come you don't do something similar to OnDevic
miu
2012/12/13 01:22:51
Done.
Good call, Dale! :-) Per hallway conversa
| |
307 DCHECK_NE(this, to); | |
308 | |
309 SyncReader* pass_me; | |
310 { | |
311 base::AutoLock auto_lock(lock_); | |
312 | |
313 // Two reasons to delay a pass: 1) another thread is currently reading from | |
314 // source_; 2) this instance is hasn't received source_ yet. | |
315 if (is_reading_from_source_ || !source_) { | |
316 DCHECK(delayed_pass_.is_null()) << "BUG: Attempt to pass twice."; | |
317 delayed_pass_ = base::Bind(&Glue::DoDelayedPassWithLockHeld, this, to); | |
318 return; | |
319 } | |
320 | |
321 pass_me = source_; | |
322 source_ = NULL; | |
323 } | |
324 | |
325 // Normal path: Pass source_ immediately. | |
326 to->ReceiveSource(pass_me); | |
327 } | |
328 | |
329 void AudioOutputController::Glue::ReceiveSource(SyncReader* source) { | |
330 base::AutoLock auto_lock(lock_); | |
331 | |
332 // Accept the audio data source. | |
333 DCHECK(!source_) | |
334 << "BUG: Attempt to receive while already holding a SyncReader."; | |
335 source_ = source; | |
336 | |
337 // Execute a delayed pass if PassSoon() scheduled one. | |
338 if (!delayed_pass_.is_null()) { | |
339 delayed_pass_.Run(); | |
340 delayed_pass_.Reset(); | |
341 } | |
342 } | |
343 | |
344 void AudioOutputController::Glue::DoDelayedPassWithLockHeld( | |
345 const scoped_refptr<Glue>& to) { | |
346 lock_.AssertAcquired(); | |
347 | |
348 // Pass the SyncReader to another Glue instance, but do not allow the current | |
349 // thread to block on this action. | |
350 controller_->message_loop_->PostTask( | |
351 FROM_HERE, base::Bind(&Glue::ReceiveSource, to, source_)); | |
352 source_ = NULL; | |
353 } | |
354 | |
355 int AudioOutputController::Glue::OnMoreData(AudioBus* dest_bus, | |
356 AudioBuffersState buffers_state) { | |
357 return OnMoreIOData(NULL, dest_bus, buffers_state); | |
358 } | |
359 | |
360 int AudioOutputController::Glue::OnMoreIOData(AudioBus* source_bus, | |
361 AudioBus* dest_bus, | |
362 AudioBuffersState buffers_state) { | |
305 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); | 363 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); |
306 | 364 |
365 // Attempt to read from the SyncReader. While reading, do not hold lock_. | |
366 // After reading, acquire the lock again; and execute a delayed pass, if | |
367 // scheduled. | |
307 { | 368 { |
308 // Check state and do nothing if we are not playing. | |
309 // We are on the hardware audio thread, so lock is needed. | |
310 base::AutoLock auto_lock(lock_); | 369 base::AutoLock auto_lock(lock_); |
311 if (state_ != kPlaying) { | 370 if (source_ && controller_->state_ == kPlaying) { |
312 return 0; | 371 is_reading_from_source_ = true; |
372 int frames_read; | |
373 { | |
374 base::AutoUnlock unlock_while_reading(lock_); | |
375 frames_read = source_->Read(source_bus, dest_bus); | |
376 source_->UpdatePendingBytes( | |
377 buffers_state.total_bytes() + | |
378 frames_read * controller_->params_.GetBytesPerFrame()); | |
379 } | |
380 is_reading_from_source_ = false; | |
381 if (!delayed_pass_.is_null()) { | |
382 delayed_pass_.Run(); | |
383 delayed_pass_.Reset(); | |
384 } | |
385 return frames_read; | |
313 } | 386 } |
314 } | 387 } |
315 | 388 |
316 int frames = sync_reader_->Read(source, dest); | 389 // We haven't received the SyncReader yet, so fill the destination with zeros. |
317 sync_reader_->UpdatePendingBytes( | 390 dest_bus->Zero(); |
318 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); | 391 return dest_bus->frames(); |
319 return frames; | |
320 } | 392 } |
321 | 393 |
322 void AudioOutputController::WaitTillDataReady() { | 394 void AudioOutputController::Glue::WaitTillDataReady() { |
323 #if defined(OS_WIN) || defined(OS_MACOSX) | 395 #if defined(OS_WIN) || defined(OS_MACOSX) |
324 base::Time start = base::Time::Now(); | 396 base::Time start = base::Time::Now(); |
325 // Wait for up to 1.5 seconds for DataReady(). 1.5 seconds was chosen because | 397 // Wait for up to 1.5 seconds for DataReady(). 1.5 seconds was chosen because |
326 // it's larger than the playback time of the WaveOut buffer size using the | 398 // it's larger than the playback time of the WaveOut buffer size using the |
327 // minimum supported sample rate: 4096 / 3000 = ~1.4 seconds. Even a client | 399 // minimum supported sample rate: 4096 / 3000 = ~1.4 seconds. Even a client |
328 // expecting real time playout should be able to fill in this time. | 400 // expecting real time playout should be able to fill in this time. |
329 const base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(1500); | 401 const base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(1500); |
330 while (!sync_reader_->DataReady() && | 402 base::AutoLock auto_lock(lock_); |
403 while (source_ && !source_->DataReady() && | |
331 ((base::Time::Now() - start) < max_wait)) { | 404 ((base::Time::Now() - start) < max_wait)) { |
405 base::AutoUnlock unlock_while_yielding(lock_); | |
332 base::PlatformThread::YieldCurrentThread(); | 406 base::PlatformThread::YieldCurrentThread(); |
333 } | 407 } |
334 #else | 408 #else |
335 // WaitTillDataReady() is deprecated and should not be used. | 409 // WaitTillDataReady() is deprecated and should not be used. |
336 CHECK(false); | 410 CHECK(false); |
337 #endif | 411 #endif |
338 } | 412 } |
339 | 413 |
340 void AudioOutputController::OnError(AudioOutputStream* stream, int code) { | 414 void AudioOutputController::Glue::OnError(AudioOutputStream* stream, int code) { |
341 // Handle error on the audio controller thread. | 415 // Handle error on the audio controller thread. |
342 message_loop_->PostTask(FROM_HERE, base::Bind( | 416 controller_->message_loop_->PostTask(FROM_HERE, base::Bind( |
343 &AudioOutputController::DoReportError, this, code)); | 417 &AudioOutputController::DoReportError, controller_, code)); |
344 } | 418 } |
345 | 419 |
346 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { | 420 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { |
347 DCHECK(message_loop_->BelongsToCurrentThread()); | 421 DCHECK(message_loop_->BelongsToCurrentThread()); |
348 | 422 |
349 // Allow calling unconditionally and bail if we don't have a stream_ to close. | 423 // Allow calling unconditionally and bail if we don't have a stream_ to close. |
350 if (stream_) { | 424 if (stream_) { |
351 stream_->Stop(); | 425 stream_->Stop(); |
352 stream_->Close(); | 426 stream_->Close(); |
353 stream_ = NULL; | 427 stream_ = NULL; |
354 | 428 |
355 audio_manager_->RemoveOutputDeviceChangeListener(this); | 429 audio_manager_->RemoveOutputDeviceChangeListener(this); |
356 audio_manager_ = NULL; | |
357 | 430 |
358 weak_this_.InvalidateWeakPtrs(); | 431 weak_this_.InvalidateWeakPtrs(); |
359 } | 432 } |
360 | 433 |
361 // Should be last in the method, do not touch "this" from here on. | 434 // Should be last in the method, do not touch "this" from here on. |
362 if (done) | 435 if (done) |
363 done->Signal(); | 436 done->Signal(); |
364 } | 437 } |
365 | 438 |
366 void AudioOutputController::OnDeviceChange() { | 439 void AudioOutputController::OnDeviceChange() { |
367 DCHECK(message_loop_->BelongsToCurrentThread()); | 440 DCHECK(message_loop_->BelongsToCurrentThread()); |
368 | 441 |
369 // We should always have a stream by this point. | 442 // We should always have a stream by this point. |
370 CHECK(stream_); | 443 CHECK(stream_); |
371 | 444 |
372 // Preserve the original state and shutdown the stream. | 445 // Preserve the original state. |
373 State original_state = state_; | 446 const State original_state = state_; |
374 stream_->Stop(); | |
375 stream_->Close(); | |
376 stream_ = NULL; | |
377 | 447 |
378 // Recreate the stream, exit if we ran into an error. | 448 // Recreate the stream (DoCreate() will first shut down an existing stream). |
449 // Exit if we ran into an error. | |
379 state_ = kRecreating; | 450 state_ = kRecreating; |
380 DoCreate(); | 451 DoCreate(); |
381 if (!stream_ || state_ == kError) | 452 if (!stream_ || state_ == kError) |
382 return; | 453 return; |
383 | 454 |
384 // Get us back to the original state or an equivalent state. | 455 // Get us back to the original state or an equivalent state. |
385 switch (original_state) { | 456 switch (original_state) { |
386 case kStarting: | 457 case kStarting: |
387 case kPlaying: | 458 case kPlaying: |
459 DoSetVolume(volume_); | |
388 DoPlay(); | 460 DoPlay(); |
389 return; | 461 return; |
390 case kCreated: | 462 case kCreated: |
391 case kPausedWhenStarting: | 463 case kPausedWhenStarting: |
392 case kPaused: | 464 case kPaused: |
393 // From the outside these three states are equivalent. | 465 // From the outside these three states are equivalent. |
394 return; | 466 return; |
395 default: | 467 default: |
396 NOTREACHED() << "Invalid original state."; | 468 NOTREACHED() << "Invalid original state."; |
397 } | 469 } |
398 } | 470 } |
399 | 471 |
472 AudioOutputStream::AudioSourceCallback* AudioOutputController::Divert() { | |
473 DCHECK(!divert_glue_) << "BUG: Already diverted!"; | |
474 divert_glue_ = new Glue(this, NULL); | |
475 stream_glue_->PassSoon(divert_glue_); | |
476 return divert_glue_; | |
477 } | |
478 | |
479 void AudioOutputController::Revert( | |
480 AudioOutputStream::AudioSourceCallback* asc) { | |
481 DCHECK_EQ(divert_glue_, asc); | |
482 divert_glue_->PassSoon(stream_glue_); | |
483 divert_glue_ = NULL; | |
484 } | |
485 | |
400 } // namespace media | 486 } // namespace media |
OLD | NEW |