Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: media/audio/audio_output_controller.cc

Issue 11413078: Tab Audio Capture: Browser-side connect/disconnect functionality. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add done closure to MirroringDestination::RemoveInput(). Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698