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

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: Remove WCAudioInputStream changes (split into another change). 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),
34 volume_(1.0), 35 volume_(1.0),
35 state_(kEmpty), 36 state_(kEmpty),
36 sync_reader_(sync_reader), 37 sync_reader_(sync_reader),
37 message_loop_(audio_manager->GetMessageLoop()), 38 message_loop_(audio_manager->GetMessageLoop()),
38 number_polling_attempts_left_(0), 39 number_polling_attempts_left_(0),
39 params_(params),
40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) { 40 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
41 } 41 }
42 42
43 AudioOutputController::~AudioOutputController() { 43 AudioOutputController::~AudioOutputController() {
44 DCHECK_EQ(kClosed, state_); 44 DCHECK_EQ(kClosed, state_);
45 45
46 if (message_loop_->BelongsToCurrentThread()) { 46 if (message_loop_->BelongsToCurrentThread()) {
47 DoStopCloseAndClearStream(NULL); 47 DoStopCloseAndClearStream(NULL);
48 } else { 48 } else {
49 // http://crbug.com/120973 49 // http://crbug.com/120973
(...skipping 13 matching lines...) Expand all
63 AudioManager* audio_manager, 63 AudioManager* audio_manager,
64 EventHandler* event_handler, 64 EventHandler* event_handler,
65 const AudioParameters& params, 65 const AudioParameters& params,
66 SyncReader* sync_reader) { 66 SyncReader* sync_reader) {
67 DCHECK(audio_manager); 67 DCHECK(audio_manager);
68 DCHECK(sync_reader); 68 DCHECK(sync_reader);
69 69
70 if (!params.IsValid() || !audio_manager) 70 if (!params.IsValid() || !audio_manager)
71 return NULL; 71 return NULL;
72 72
73 // Starts the audio controller thread.
74 scoped_refptr<AudioOutputController> controller(new AudioOutputController( 73 scoped_refptr<AudioOutputController> controller(new AudioOutputController(
75 audio_manager, event_handler, params, sync_reader)); 74 audio_manager, event_handler, params, sync_reader));
76
77 controller->message_loop_->PostTask(FROM_HERE, base::Bind( 75 controller->message_loop_->PostTask(FROM_HERE, base::Bind(
78 &AudioOutputController::DoCreate, controller)); 76 &AudioOutputController::DoCreate, controller));
79
80 return controller; 77 return controller;
81 } 78 }
82 79
83 void AudioOutputController::Play() { 80 void AudioOutputController::Play() {
84 DCHECK(message_loop_); 81 DCHECK(message_loop_);
85 message_loop_->PostTask(FROM_HERE, base::Bind( 82 message_loop_->PostTask(FROM_HERE, base::Bind(
86 &AudioOutputController::DoPlay, this)); 83 &AudioOutputController::DoPlay, this));
87 } 84 }
88 85
89 void AudioOutputController::Pause() { 86 void AudioOutputController::Pause() {
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
131 128
132 if (!stream_->Open()) { 129 if (!stream_->Open()) {
133 state_ = kError; 130 state_ = kError;
134 DoStopCloseAndClearStream(NULL); 131 DoStopCloseAndClearStream(NULL);
135 132
136 // TODO(hclam): Define error types. 133 // TODO(hclam): Define error types.
137 handler_->OnError(this, 0); 134 handler_->OnError(this, 0);
138 return; 135 return;
139 } 136 }
140 137
141 // Everything started okay, so register for state change callbacks if we have 138 // Everything started okay, so re-register for state change callbacks (the
142 // not already done so. 139 // call to DoStopCloseAndClearStream() above de-registered this instance).
143 if (state_ != kRecreating) 140 audio_manager_->AddOutputDeviceChangeListener(this);
DaleCurtis 2012/12/05 23:35:14 I don't see where you're removing the device chang
miu 2012/12/11 02:30:45 The call to DoStopCloseAndClearStream() above call
144 audio_manager_->AddOutputDeviceChangeListener(this);
145 141
146 // We have successfully opened the stream. Set the initial volume. 142 // We have successfully opened the stream. Set the initial volume.
147 stream_->SetVolume(volume_); 143 stream_->SetVolume(volume_);
148 144
149 // Finally set the state to kCreated. 145 // Finally set the state to kCreated.
150 State original_state = state_; 146 State original_state = state_;
151 state_ = kCreated; 147 state_ = kCreated;
152 148
153 // And then report we have been created if we haven't done so already. 149 // And then report we have been created if we haven't done so already.
154 if (original_state != kRecreating) 150 if (original_state != kRecreating)
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 weak_this_.GetWeakPtr()), 204 weak_this_.GetWeakPtr()),
209 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds)); 205 TimeDelta::FromMilliseconds(kPollPauseInMilliseconds));
210 } 206 }
211 } 207 }
212 208
213 void AudioOutputController::StartStream() { 209 void AudioOutputController::StartStream() {
214 DCHECK(message_loop_->BelongsToCurrentThread()); 210 DCHECK(message_loop_->BelongsToCurrentThread());
215 state_ = kPlaying; 211 state_ = kPlaying;
216 212
217 // We start the AudioOutputStream lazily. 213 // We start the AudioOutputStream lazily.
218 stream_->Start(this); 214 if (stream_)
DaleCurtis 2012/12/05 23:35:14 stream_ should always be valid here. why not check
miu 2012/12/11 02:30:45 I had changed the semantics so that stream_ is NUL
215 stream_->Start(this);
219 216
220 // Tell the event handler that we are now playing. 217 // Tell the event handler that we are now playing.
221 handler_->OnPlaying(this); 218 handler_->OnPlaying(this);
222 } 219 }
223 220
224 void AudioOutputController::DoPause() { 221 void AudioOutputController::DoPause() {
225 DCHECK(message_loop_->BelongsToCurrentThread()); 222 DCHECK(message_loop_->BelongsToCurrentThread());
226 223
227 if (stream_) { 224 if (stream_) {
228 // Then we stop the audio device. This is not the perfect solution 225 // Then we stop the audio device. This is not the perfect solution
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 AudioBuffersState buffers_state) { 295 AudioBuffersState buffers_state) {
299 return OnMoreIOData(NULL, dest, buffers_state); 296 return OnMoreIOData(NULL, dest, buffers_state);
300 } 297 }
301 298
302 int AudioOutputController::OnMoreIOData(AudioBus* source, 299 int AudioOutputController::OnMoreIOData(AudioBus* source,
303 AudioBus* dest, 300 AudioBus* dest,
304 AudioBuffersState buffers_state) { 301 AudioBuffersState buffers_state) {
305 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData"); 302 TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData");
306 303
307 { 304 {
308 // Check state and do nothing if we are not playing. 305 // Check state. When not playing, fill the destination with zeros.
309 // We are on the hardware audio thread, so lock is needed. 306 // We are on the hardware audio thread, so lock is needed.
310 base::AutoLock auto_lock(lock_); 307 base::AutoLock auto_lock(lock_);
311 if (state_ != kPlaying) { 308 if (state_ != kPlaying) {
312 return 0; 309 dest->Zero();
310 return dest->frames();
313 } 311 }
314 } 312 }
315 313
316 int frames = sync_reader_->Read(source, dest); 314 int frames = sync_reader_->Read(source, dest);
317 sync_reader_->UpdatePendingBytes( 315 sync_reader_->UpdatePendingBytes(
318 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame()); 316 buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
319 return frames; 317 return frames;
320 } 318 }
321 319
322 void AudioOutputController::WaitTillDataReady() { 320 void AudioOutputController::WaitTillDataReady() {
(...skipping 23 matching lines...) Expand all
346 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) { 344 void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) {
347 DCHECK(message_loop_->BelongsToCurrentThread()); 345 DCHECK(message_loop_->BelongsToCurrentThread());
348 346
349 // Allow calling unconditionally and bail if we don't have a stream_ to close. 347 // Allow calling unconditionally and bail if we don't have a stream_ to close.
350 if (stream_) { 348 if (stream_) {
351 stream_->Stop(); 349 stream_->Stop();
352 stream_->Close(); 350 stream_->Close();
353 stream_ = NULL; 351 stream_ = NULL;
354 352
355 audio_manager_->RemoveOutputDeviceChangeListener(this); 353 audio_manager_->RemoveOutputDeviceChangeListener(this);
356 audio_manager_ = NULL;
357 354
358 weak_this_.InvalidateWeakPtrs(); 355 weak_this_.InvalidateWeakPtrs();
359 } 356 }
360 357
361 // Should be last in the method, do not touch "this" from here on. 358 // Should be last in the method, do not touch "this" from here on.
362 if (done) 359 if (done)
363 done->Signal(); 360 done->Signal();
364 } 361 }
365 362
366 void AudioOutputController::OnDeviceChange() { 363 void AudioOutputController::OnDeviceChange() {
367 DCHECK(message_loop_->BelongsToCurrentThread()); 364 DCHECK(message_loop_->BelongsToCurrentThread());
368 365
369 // We should always have a stream by this point. 366 // Preserve the original state.
DaleCurtis 2012/12/05 23:35:14 Is this not true anymore?
miu 2012/12/11 02:30:45 Done. (Reverted back to original behavior.)
370 CHECK(stream_); 367 State original_state = state_;
371 368
372 // Preserve the original state and shutdown the stream. 369 // Recreate the stream (DoCreate() will first shut down an existing stream).
373 State original_state = state_; 370 // Exit if we ran into an error.
374 stream_->Stop();
375 stream_->Close();
376 stream_ = NULL;
377
378 // Recreate the stream, exit if we ran into an error.
379 state_ = kRecreating; 371 state_ = kRecreating;
380 DoCreate(); 372 DoCreate();
381 if (!stream_ || state_ == kError) 373 if (!stream_ || state_ == kError)
382 return; 374 return;
383 375
384 // Get us back to the original state or an equivalent state. 376 // Get us back to the original state or an equivalent state.
385 switch (original_state) { 377 switch (original_state) {
386 case kStarting: 378 case kStarting:
387 case kPlaying: 379 case kPlaying:
380 DoSetVolume(volume_);
388 DoPlay(); 381 DoPlay();
389 return; 382 return;
390 case kCreated: 383 case kCreated:
391 case kPausedWhenStarting: 384 case kPausedWhenStarting:
392 case kPaused: 385 case kPaused:
393 // From the outside these three states are equivalent. 386 // From the outside these three states are equivalent.
394 return; 387 return;
395 default: 388 default:
396 NOTREACHED() << "Invalid original state."; 389 NOTREACHED() << "Invalid original state.";
397 } 390 }
398 } 391 }
399 392
393 AudioOutputStream::AudioSourceCallback* AudioOutputController::Divert() {
394 // Assumption: AudioRendererHost is calling this method on the IO
395 // BrowserThread.
396 DCHECK(!message_loop_->BelongsToCurrentThread());
DaleCurtis 2012/12/05 23:35:14 Is this helpful? It doesn't really matter what thr
miu 2012/12/11 02:30:45 Done.
397
398 // Block until stream closure is complete because, otherwise, it's possible
399 // for two entities to be pulling audio data at the same time. In addition,
400 // a side effect of closing the stream is that this controller will be
401 // de-registered from device change events (from AudioManager) during the
402 // "divert."
403 base::WaitableEvent blocker(true, false);
404 message_loop_->PostTask(
405 FROM_HERE,
406 base::Bind(&AudioOutputController::DoStopCloseAndClearStream, this,
407 &blocker));
408 blocker.Wait();
DaleCurtis 2012/12/05 23:35:14 Hmmm, seems risky. You're blocking the IO thread w
miu 2012/12/11 02:30:45 I changed my approach, and came up with something
409
410 return this;
411 }
412
413 void AudioOutputController::Revert(
414 AudioOutputStream::AudioSourceCallback* asc) {
415 // Assumption: AudioRendererHost is calling this method on the IO
416 // BrowserThread.
417 DCHECK(!message_loop_->BelongsToCurrentThread());
418
419 DCHECK_EQ(this, asc);
DaleCurtis 2012/12/05 23:35:14 Again seems odd.
miu 2012/12/11 02:30:45 Done.
420
421 // The following re-creates the normal audio output stream and places it in
422 // the correct playback state. It also re-registers this controller for
423 // device change events from AudioManager.
424 message_loop_->PostTask(
425 FROM_HERE, base::Bind(&AudioOutputController::OnDeviceChange, this));
426 }
427
400 } // namespace media 428 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698