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" |
11 #include "base/threading/platform_thread.h" | 11 #include "base/threading/platform_thread.h" |
12 #include "base/threading/thread_restrictions.h" | 12 #include "base/threading/thread_restrictions.h" |
13 #include "base/time.h" | 13 #include "base/time.h" |
14 #include "build/build_config.h" | 14 #include "build/build_config.h" |
15 #include "media/audio/shared_memory_util.h" | 15 #include "media/audio/shared_memory_util.h" |
16 | 16 |
17 using base::Time; | 17 using base::Time; |
18 using base::TimeDelta; | 18 using base::TimeDelta; |
19 using base::WaitableEvent; | 19 using base::WaitableEvent; |
20 | 20 |
21 namespace media { | 21 namespace media { |
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 // An AudioSourceCallback that allows passing around the underlying SyncReader |
| 28 // data source between consumers of the data. It does this in a "pass or |
| 29 // delayed pass" scheme that ensures synchronization between timing-sensitive |
| 30 // threads is effectively non-blocking. In addition, SourceSharingCallback |
| 31 // ensures that only one consumer is pulling data from the SyncReader at a time. |
| 32 class AudioOutputController::SourceSharingCallback |
| 33 : public AudioOutputStream::AudioSourceCallback, |
| 34 public base::RefCountedThreadSafe<SourceSharingCallback> { |
| 35 public: |
| 36 SourceSharingCallback(AudioOutputController* controller, SyncReader* source); |
| 37 |
| 38 // Pass the SyncReader to another instance as soon as possible. |
| 39 void PassSoon(const scoped_refptr<SourceSharingCallback>& receiver); |
| 40 |
| 41 // AudioSourceCallback implementation. |
| 42 virtual int OnMoreData(AudioBus* dest_bus, |
| 43 AudioBuffersState buffers_state) OVERRIDE; |
| 44 virtual int OnMoreIOData(AudioBus* source_bus, |
| 45 AudioBus* dest_bus, |
| 46 AudioBuffersState buffers_state) OVERRIDE; |
| 47 virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE; |
| 48 virtual void WaitTillDataReady() OVERRIDE; |
| 49 |
| 50 private: |
| 51 friend class base::RefCountedThreadSafe<SourceSharingCallback>; |
| 52 virtual ~SourceSharingCallback(); |
| 53 |
| 54 // Take over reading from |source|. |
| 55 void ReceiveSource(SyncReader* source); |
| 56 |
| 57 // Executes a delayed pass (i.e., a pass that was delayed because source_ was |
| 58 // in-use at the time PassSoon() was called). |
| 59 void DoDelayedPassWithLockHeld( |
| 60 const scoped_refptr<SourceSharingCallback>& to); |
| 61 |
| 62 // Access to parent/owner instance. |
| 63 AudioOutputController* const controller_; |
| 64 |
| 65 // Guards passing of SyncReader to another SourceSharingCallback instance. |
| 66 // The implementation should acquire lock_ for only very short operations |
| 67 // since audio threads are timing-sensitive. |
| 68 base::Lock lock_; |
| 69 |
| 70 // Audio data source, or NULL if none yet. |
| 71 SyncReader* source_; |
| 72 |
| 73 // True while source_ is in-use without lock_ held. |
| 74 bool is_reading_from_source_; |
| 75 |
| 76 // When !delayed_pass_.is_null(), source_ is scheduled to be passed as soon as |
| 77 // possible. |
| 78 base::Closure delayed_pass_; |
| 79 |
| 80 DISALLOW_COPY_AND_ASSIGN(SourceSharingCallback); |
| 81 }; |
| 82 |
27 AudioOutputController::AudioOutputController(AudioManager* audio_manager, | 83 AudioOutputController::AudioOutputController(AudioManager* audio_manager, |
28 EventHandler* handler, | 84 EventHandler* handler, |
29 const AudioParameters& params, | 85 const AudioParameters& params, |
30 SyncReader* sync_reader) | 86 SyncReader* sync_reader) |
31 : audio_manager_(audio_manager), | 87 : audio_manager_(audio_manager), |
| 88 params_(params), |
32 handler_(handler), | 89 handler_(handler), |
33 stream_(NULL), | 90 stream_(NULL), |
| 91 callback_for_stream_(new SourceSharingCallback(this, sync_reader)), |
34 volume_(1.0), | 92 volume_(1.0), |
35 state_(kEmpty), | 93 state_(kEmpty), |
36 sync_reader_(sync_reader), | 94 sync_reader_(sync_reader), |
37 message_loop_(audio_manager->GetMessageLoop()), | 95 message_loop_(audio_manager->GetMessageLoop()), |
38 number_polling_attempts_left_(0), | 96 number_polling_attempts_left_(0), |
39 params_(params), | |
40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { | 97 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { |
41 } | 98 } |
42 | 99 |
43 AudioOutputController::~AudioOutputController() { | 100 AudioOutputController::~AudioOutputController() { |
44 DCHECK_EQ(kClosed, state_); | 101 DCHECK_EQ(kClosed, state_); |
45 | 102 |
46 if (message_loop_->BelongsToCurrentThread()) { | 103 if (message_loop_->BelongsToCurrentThread()) { |
47 DoStopCloseAndClearStream(NULL); | 104 DoStopCloseAndClearStream(NULL); |
48 } else { | 105 } else { |
49 // http://crbug.com/120973 | 106 // http://crbug.com/120973 |
(...skipping 13 matching lines...) Expand all Loading... |
63 AudioManager* audio_manager, | 120 AudioManager* audio_manager, |
64 EventHandler* event_handler, | 121 EventHandler* event_handler, |
65 const AudioParameters& params, | 122 const AudioParameters& params, |
66 SyncReader* sync_reader) { | 123 SyncReader* sync_reader) { |
67 DCHECK(audio_manager); | 124 DCHECK(audio_manager); |
68 DCHECK(sync_reader); | 125 DCHECK(sync_reader); |
69 | 126 |
70 if (!params.IsValid() || !audio_manager) | 127 if (!params.IsValid() || !audio_manager) |
71 return NULL; | 128 return NULL; |
72 | 129 |
73 // Starts the audio controller thread. | |
74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( | 130 scoped_refptr<AudioOutputController> controller(new AudioOutputController( |
75 audio_manager, event_handler, params, sync_reader)); | 131 audio_manager, event_handler, params, sync_reader)); |
76 | |
77 controller->message_loop_->PostTask(FROM_HERE, base::Bind( | 132 controller->message_loop_->PostTask(FROM_HERE, base::Bind( |
78 &AudioOutputController::DoCreate, controller)); | 133 &AudioOutputController::DoCreate, controller)); |
79 | |
80 return controller; | 134 return controller; |
81 } | 135 } |
82 | 136 |
83 void AudioOutputController::Play() { | 137 void AudioOutputController::Play() { |
84 DCHECK(message_loop_); | 138 DCHECK(message_loop_); |
85 message_loop_->PostTask(FROM_HERE, base::Bind( | 139 message_loop_->PostTask(FROM_HERE, base::Bind( |
86 &AudioOutputController::DoPlay, this)); | 140 &AudioOutputController::DoPlay, this)); |
87 } | 141 } |
88 | 142 |
89 void AudioOutputController::Pause() { | 143 void AudioOutputController::Pause() { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 | 185 |
132 if (!stream_->Open()) { | 186 if (!stream_->Open()) { |
133 state_ = kError; | 187 state_ = kError; |
134 DoStopCloseAndClearStream(NULL); | 188 DoStopCloseAndClearStream(NULL); |
135 | 189 |
136 // TODO(hclam): Define error types. | 190 // TODO(hclam): Define error types. |
137 handler_->OnError(this, 0); | 191 handler_->OnError(this, 0); |
138 return; | 192 return; |
139 } | 193 } |
140 | 194 |
141 // Everything started okay, so register for state change callbacks if we have | 195 // Everything started okay, so re-register for state change callbacks. Note: |
142 // not already done so. | 196 // The call to DoStopCloseAndClearStream() above called |
143 if (state_ != kRecreating) | 197 // RemoveOutputDeviceChangeListener(). |
144 audio_manager_->AddOutputDeviceChangeListener(this); | 198 audio_manager_->AddOutputDeviceChangeListener(this); |
145 | 199 |
146 // We have successfully opened the stream. Set the initial volume. | 200 // We have successfully opened the stream. Set the initial volume. |
147 stream_->SetVolume(volume_); | 201 stream_->SetVolume(volume_); |
148 | 202 |
149 // Finally set the state to kCreated. | 203 // Finally set the state to kCreated. |
150 State original_state = state_; | 204 State original_state = state_; |
151 state_ = kCreated; | 205 state_ = kCreated; |
152 | 206 |
153 // And then report we have been created if we haven't done so already. | 207 // And then report we have been created if we haven't done so already. |
154 if (original_state != kRecreating) | 208 if (original_state != kRecreating) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 weak_this_.GetWeakPtr()), | 262 weak_this_.GetWeakPtr()), |
209 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); | 263 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); |
210 } | 264 } |
211 } | 265 } |
212 | 266 |
213 void AudioOutputController::StartStream() { | 267 void AudioOutputController::StartStream() { |
214 DCHECK(message_loop_->BelongsToCurrentThread()); | 268 DCHECK(message_loop_->BelongsToCurrentThread()); |
215 state_ = kPlaying; | 269 state_ = kPlaying; |
216 | 270 |
217 // We start the AudioOutputStream lazily. | 271 // We start the AudioOutputStream lazily. |
218 stream_->Start(this); | 272 stream_->Start(callback_for_stream_); |
219 | 273 |
220 // Tell the event handler that we are now playing. | 274 // Tell the event handler that we are now playing. |
221 handler_->OnPlaying(this); | 275 handler_->OnPlaying(this); |
222 } | 276 } |
223 | 277 |
224 void AudioOutputController::DoPause() { | 278 void AudioOutputController::DoPause() { |
225 DCHECK(message_loop_->BelongsToCurrentThread()); | 279 DCHECK(message_loop_->BelongsToCurrentThread()); |
226 | 280 |
227 if (stream_) { | 281 if (stream_) { |
228 // Then we stop the audio device. This is not the perfect solution | 282 // Then we stop the audio device. This is not the perfect solution |
(...skipping 26 matching lines...) Expand all Loading... |
255 void AudioOutputController::DoFlush() { | 309 void AudioOutputController::DoFlush() { |
256 DCHECK(message_loop_->BelongsToCurrentThread()); | 310 DCHECK(message_loop_->BelongsToCurrentThread()); |
257 | 311 |
258 // TODO(hclam): Actually flush the audio device. | 312 // TODO(hclam): Actually flush the audio device. |
259 } | 313 } |
260 | 314 |
261 void AudioOutputController::DoClose() { | 315 void AudioOutputController::DoClose() { |
262 DCHECK(message_loop_->BelongsToCurrentThread()); | 316 DCHECK(message_loop_->BelongsToCurrentThread()); |
263 | 317 |
264 if (state_ != kClosed) { | 318 if (state_ != kClosed) { |
| 319 DCHECK(!callback_for_divert_); |
265 DoStopCloseAndClearStream(NULL); | 320 DoStopCloseAndClearStream(NULL); |
266 sync_reader_->Close(); | 321 sync_reader_->Close(); |
267 state_ = kClosed; | 322 state_ = kClosed; |
268 } | 323 } |
269 } | 324 } |
270 | 325 |
271 void AudioOutputController::DoSetVolume(double volume) { | 326 void AudioOutputController::DoSetVolume(double volume) { |
272 DCHECK(message_loop_->BelongsToCurrentThread()); | 327 DCHECK(message_loop_->BelongsToCurrentThread()); |
273 | 328 |
274 // Saves the volume to a member first. We may not be able to set the volume | 329 // 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; | 342 return; |
288 } | 343 } |
289 } | 344 } |
290 | 345 |
291 void AudioOutputController::DoReportError(int code) { | 346 void AudioOutputController::DoReportError(int code) { |
292 DCHECK(message_loop_->BelongsToCurrentThread()); | 347 DCHECK(message_loop_->BelongsToCurrentThread()); |
293 if (state_ != kClosed) | 348 if (state_ != kClosed) |
294 handler_->OnError(this, code); | 349 handler_->OnError(this, code); |
295 } | 350 } |
296 | 351 |
297 int AudioOutputController::OnMoreData(AudioBus* dest, | 352 AudioOutputController::SourceSharingCallback::SourceSharingCallback( |
298 AudioBuffersState buffers_state) { | 353 AudioOutputController* controller, SyncReader* source) |
299 return OnMoreIOData(NULL, dest, buffers_state); | 354 : controller_(controller), |
| 355 source_(source), |
| 356 is_reading_from_source_(false) { |
300 } | 357 } |
301 | 358 |
302 int AudioOutputController::OnMoreIOData(AudioBus* source, | 359 AudioOutputController::SourceSharingCallback::~SourceSharingCallback() { |
303 AudioBus* dest, | 360 } |
304 AudioBuffersState buffers_state) { | 361 |
| 362 void AudioOutputController::SourceSharingCallback::PassSoon( |
| 363 const scoped_refptr<SourceSharingCallback>& to) { |
| 364 DCHECK_NE(this, to); |
| 365 |
| 366 SyncReader* pass_me; |
| 367 { |
| 368 base::AutoLock auto_lock(lock_); |
| 369 |
| 370 // Two reasons to delay a pass: 1) another thread is currently reading from |
| 371 // source_; 2) this instance is hasn't received source_ yet. |
| 372 if (is_reading_from_source_ || !source_) { |
| 373 DCHECK(delayed_pass_.is_null()) << "BUG: Attempt to pass twice."; |
| 374 delayed_pass_ = base::Bind( |
| 375 &SourceSharingCallback::DoDelayedPassWithLockHeld, this, to); |
| 376 return; |
| 377 } |
| 378 |
| 379 pass_me = source_; |
| 380 source_ = NULL; |
| 381 } |
| 382 |
| 383 // Normal path: Pass source_ immediately. |
| 384 to->ReceiveSource(pass_me); |
| 385 } |
| 386 |
| 387 void AudioOutputController::SourceSharingCallback::ReceiveSource( |
| 388 SyncReader* source) { |
| 389 base::AutoLock auto_lock(lock_); |
| 390 |
| 391 // Accept the audio data source. |
| 392 DCHECK(!source_) |
| 393 << "BUG: Attempt to receive while already holding a SyncReader."; |
| 394 source_ = source; |
| 395 |
| 396 // Execute a delayed pass if PassSoon() scheduled one. |
| 397 if (!delayed_pass_.is_null()) { |
| 398 delayed_pass_.Run(); |
| 399 delayed_pass_.Reset(); |
| 400 } |
| 401 } |
| 402 |
| 403 void AudioOutputController::SourceSharingCallback::DoDelayedPassWithLockHeld( |
| 404 const scoped_refptr<SourceSharingCallback>& to) { |
| 405 lock_.AssertAcquired(); |
| 406 |
| 407 // Pass the SyncReader to another SourceSharingCallback instance, but do not |
| 408 // allow the current thread to block on this action. |
| 409 controller_->message_loop_->PostTask( |
| 410 FROM_HERE, |
| 411 base::Bind(&SourceSharingCallback::ReceiveSource, to, source_)); |
| 412 source_ = NULL; |
| 413 } |
| 414 |
| 415 int AudioOutputController::SourceSharingCallback::OnMoreData( |
| 416 AudioBus* dest_bus, AudioBuffersState buffers_state) { |
| 417 return OnMoreIOData(NULL, dest_bus, buffers_state); |
| 418 } |
| 419 |
| 420 int AudioOutputController::SourceSharingCallback::OnMoreIOData( |
| 421 AudioBus* source_bus, AudioBus* dest_bus, AudioBuffersState buffers_state) { |
305 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); | 422 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); |
306 | 423 |
| 424 // Attempt to read from the SyncReader. While reading, do not hold lock_. |
| 425 // After reading, acquire the lock again; and execute a delayed pass, if |
| 426 // scheduled. |
307 { | 427 { |
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_); | 428 base::AutoLock auto_lock(lock_); |
311 if (state_ != kPlaying) { | 429 if (source_ && controller_->state_ == kPlaying) { |
312 return 0; | 430 is_reading_from_source_ = true; |
| 431 int frames_read; |
| 432 { |
| 433 base::AutoUnlock unlock_while_reading(lock_); |
| 434 frames_read = source_->Read(source_bus, dest_bus); |
| 435 source_->UpdatePendingBytes( |
| 436 buffers_state.total_bytes() + |
| 437 frames_read * controller_->params_.GetBytesPerFrame()); |
| 438 } |
| 439 is_reading_from_source_ = false; |
| 440 if (!delayed_pass_.is_null()) { |
| 441 delayed_pass_.Run(); |
| 442 delayed_pass_.Reset(); |
| 443 } |
| 444 return frames_read; |
313 } | 445 } |
314 } | 446 } |
315 | 447 |
316 int frames = sync_reader_->Read(source, dest); | 448 // We haven't received the SyncReader yet, so fill the destination with zeros. |
317 sync_reader_->UpdatePendingBytes( | 449 dest_bus->Zero(); |
318 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); | 450 return dest_bus->frames(); |
319 return frames; | |
320 } | 451 } |
321 | 452 |
322 void AudioOutputController::WaitTillDataReady() { | 453 void AudioOutputController::SourceSharingCallback::WaitTillDataReady() { |
323 #if defined(OS_WIN) || defined(OS_MACOSX) | 454 #if defined(OS_WIN) || defined(OS_MACOSX) |
324 base::Time start = base::Time::Now(); | 455 base::Time start = base::Time::Now(); |
325 // Wait for up to 1.5 seconds for DataReady(). 1.5 seconds was chosen because | 456 // 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 | 457 // 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 | 458 // 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. | 459 // expecting real time playout should be able to fill in this time. |
329 const base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(1500); | 460 const base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(1500); |
330 while (!sync_reader_->DataReady() && | 461 base::AutoLock auto_lock(lock_); |
| 462 while (source_ && !source_->DataReady() && |
331 ((base::Time::Now() - start) < max_wait)) { | 463 ((base::Time::Now() - start) < max_wait)) { |
| 464 base::AutoUnlock unlock_while_yielding(lock_); |
332 base::PlatformThread::YieldCurrentThread(); | 465 base::PlatformThread::YieldCurrentThread(); |
333 } | 466 } |
334 #else | 467 #else |
335 // WaitTillDataReady() is deprecated and should not be used. | 468 // WaitTillDataReady() is deprecated and should not be used. |
336 CHECK(false); | 469 CHECK(false); |
337 #endif | 470 #endif |
338 } | 471 } |
339 | 472 |
340 void AudioOutputController::OnError(AudioOutputStream* stream, int code) { | 473 void AudioOutputController::SourceSharingCallback::OnError( |
| 474 AudioOutputStream* stream, int code) { |
341 // Handle error on the audio controller thread. | 475 // Handle error on the audio controller thread. |
342 message_loop_->PostTask(FROM_HERE, base::Bind( | 476 controller_->message_loop_->PostTask(FROM_HERE, base::Bind( |
343 &AudioOutputController::DoReportError, this, code)); | 477 &AudioOutputController::DoReportError, controller_, code)); |
344 } | 478 } |
345 | 479 |
346 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { | 480 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { |
347 DCHECK(message_loop_->BelongsToCurrentThread()); | 481 DCHECK(message_loop_->BelongsToCurrentThread()); |
348 | 482 |
349 // Allow calling unconditionally and bail if we don't have a stream_ to close. | 483 // Allow calling unconditionally and bail if we don't have a stream_ to close. |
350 if (stream_) { | 484 if (stream_) { |
351 stream_->Stop(); | 485 stream_->Stop(); |
352 stream_->Close(); | 486 stream_->Close(); |
353 stream_ = NULL; | 487 stream_ = NULL; |
354 | 488 |
355 audio_manager_->RemoveOutputDeviceChangeListener(this); | 489 audio_manager_->RemoveOutputDeviceChangeListener(this); |
356 audio_manager_ = NULL; | |
357 | 490 |
358 weak_this_.InvalidateWeakPtrs(); | 491 weak_this_.InvalidateWeakPtrs(); |
359 } | 492 } |
360 | 493 |
361 // Should be last in the method, do not touch "this" from here on. | 494 // Should be last in the method, do not touch "this" from here on. |
362 if (done) | 495 if (done) |
363 done->Signal(); | 496 done->Signal(); |
364 } | 497 } |
365 | 498 |
366 void AudioOutputController::OnDeviceChange() { | 499 void AudioOutputController::OnDeviceChange() { |
367 DCHECK(message_loop_->BelongsToCurrentThread()); | 500 DCHECK(message_loop_->BelongsToCurrentThread()); |
368 | 501 |
369 // We should always have a stream by this point. | 502 // We should always have a stream by this point. |
370 CHECK(stream_); | 503 CHECK(stream_); |
371 | 504 |
372 // Preserve the original state and shutdown the stream. | 505 // Preserve the original state. |
373 State original_state = state_; | 506 const State original_state = state_; |
374 stream_->Stop(); | |
375 stream_->Close(); | |
376 stream_ = NULL; | |
377 | 507 |
378 // Recreate the stream, exit if we ran into an error. | 508 // Recreate the stream (DoCreate() will first shut down an existing stream). |
| 509 // Exit if we ran into an error. |
379 state_ = kRecreating; | 510 state_ = kRecreating; |
380 DoCreate(); | 511 DoCreate(); |
381 if (!stream_ || state_ == kError) | 512 if (!stream_ || state_ == kError) |
382 return; | 513 return; |
383 | 514 |
384 // Get us back to the original state or an equivalent state. | 515 // Get us back to the original state or an equivalent state. |
385 switch (original_state) { | 516 switch (original_state) { |
386 case kStarting: | 517 case kStarting: |
387 case kPlaying: | 518 case kPlaying: |
| 519 DoSetVolume(volume_); |
388 DoPlay(); | 520 DoPlay(); |
389 return; | 521 return; |
390 case kCreated: | 522 case kCreated: |
391 case kPausedWhenStarting: | 523 case kPausedWhenStarting: |
392 case kPaused: | 524 case kPaused: |
393 // From the outside these three states are equivalent. | 525 // From the outside these three states are equivalent. |
394 return; | 526 return; |
395 default: | 527 default: |
396 NOTREACHED() << "Invalid original state."; | 528 NOTREACHED() << "Invalid original state."; |
397 } | 529 } |
398 } | 530 } |
399 | 531 |
| 532 AudioOutputStream::AudioSourceCallback* AudioOutputController::Divert() { |
| 533 DCHECK(!callback_for_divert_) << "BUG: Already diverted!"; |
| 534 callback_for_divert_ = new SourceSharingCallback(this, NULL); |
| 535 callback_for_stream_->PassSoon(callback_for_divert_); |
| 536 return callback_for_divert_; |
| 537 } |
| 538 |
| 539 void AudioOutputController::Revert( |
| 540 AudioOutputStream::AudioSourceCallback* asc) { |
| 541 DCHECK_EQ(callback_for_divert_, asc); |
| 542 callback_for_divert_->PassSoon(callback_for_stream_); |
| 543 callback_for_divert_ = NULL; |
| 544 } |
| 545 |
400 } // namespace media | 546 } // namespace media |
OLD | NEW |