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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: media/audio/audio_output_controller.cc
diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc
index 50850c9265cdd2b0f503e97994a122b70167047d..0120d0e5e37c276d20bf0c345dff876dd5f218ce 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -29,14 +29,15 @@ AudioOutputController::AudioOutputController(AudioManager* audio_manager,
const AudioParameters& params,
SyncReader* sync_reader)
: audio_manager_(audio_manager),
+ params_(params),
handler_(handler),
stream_(NULL),
+ stream_glue_(new Glue(this, sync_reader)),
volume_(1.0),
state_(kEmpty),
sync_reader_(sync_reader),
message_loop_(audio_manager->GetMessageLoop()),
number_polling_attempts_left_(0),
- params_(params),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
}
@@ -70,13 +71,10 @@ scoped_refptr<AudioOutputController> AudioOutputController::Create(
if (!params.IsValid() || !audio_manager)
return NULL;
- // Starts the audio controller thread.
scoped_refptr<AudioOutputController> controller(new AudioOutputController(
audio_manager, event_handler, params, sync_reader));
-
controller->message_loop_->PostTask(FROM_HERE, base::Bind(
&AudioOutputController::DoCreate, controller));
-
return controller;
}
@@ -138,10 +136,10 @@ void AudioOutputController::DoCreate() {
return;
}
- // Everything started okay, so register for state change callbacks if we have
- // not already done so.
- if (state_ != kRecreating)
- audio_manager_->AddOutputDeviceChangeListener(this);
+ // Everything started okay, so re-register for state change callbacks. Note:
+ // The call to DoStopCloseAndClearStream() above called
+ // RemoveOutputDeviceChangeListener().
+ audio_manager_->AddOutputDeviceChangeListener(this);
// We have successfully opened the stream. Set the initial volume.
stream_->SetVolume(volume_);
@@ -215,7 +213,7 @@ void AudioOutputController::StartStream() {
state_ = kPlaying;
// We start the AudioOutputStream lazily.
- stream_->Start(this);
+ stream_->Start(stream_glue_);
// Tell the event handler that we are now playing.
handler_->OnPlaying(this);
@@ -262,6 +260,7 @@ void AudioOutputController::DoClose() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ != kClosed) {
+ DCHECK(!divert_glue_);
DoStopCloseAndClearStream(NULL);
sync_reader_->Close();
state_ = kClosed;
@@ -294,32 +293,105 @@ void AudioOutputController::DoReportError(int code) {
handler_->OnError(this, code);
}
-int AudioOutputController::OnMoreData(AudioBus* dest,
- AudioBuffersState buffers_state) {
- return OnMoreIOData(NULL, dest, buffers_state);
+AudioOutputController::Glue::Glue(AudioOutputController* controller,
+ SyncReader* source)
+ : controller_(controller),
+ source_(source),
+ is_reading_from_source_(false) {
+}
+
+AudioOutputController::Glue::~Glue() {
+}
+
+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
+ DCHECK_NE(this, to);
+
+ SyncReader* pass_me;
+ {
+ base::AutoLock auto_lock(lock_);
+
+ // Two reasons to delay a pass: 1) another thread is currently reading from
+ // source_; 2) this instance is hasn't received source_ yet.
+ if (is_reading_from_source_ || !source_) {
+ DCHECK(delayed_pass_.is_null()) << "BUG: Attempt to pass twice.";
+ delayed_pass_ = base::Bind(&Glue::DoDelayedPassWithLockHeld, this, to);
+ return;
+ }
+
+ pass_me = source_;
+ source_ = NULL;
+ }
+
+ // Normal path: Pass source_ immediately.
+ to->ReceiveSource(pass_me);
}
-int AudioOutputController::OnMoreIOData(AudioBus* source,
- AudioBus* dest,
- AudioBuffersState buffers_state) {
+void AudioOutputController::Glue::ReceiveSource(SyncReader* source) {
+ base::AutoLock auto_lock(lock_);
+
+ // Accept the audio data source.
+ DCHECK(!source_)
+ << "BUG: Attempt to receive while already holding a SyncReader.";
+ source_ = source;
+
+ // Execute a delayed pass if PassSoon() scheduled one.
+ if (!delayed_pass_.is_null()) {
+ delayed_pass_.Run();
+ delayed_pass_.Reset();
+ }
+}
+
+void AudioOutputController::Glue::DoDelayedPassWithLockHeld(
+ const scoped_refptr<Glue>& to) {
+ lock_.AssertAcquired();
+
+ // Pass the SyncReader to another Glue instance, but do not allow the current
+ // thread to block on this action.
+ controller_->message_loop_->PostTask(
+ FROM_HERE, base::Bind(&Glue::ReceiveSource, to, source_));
+ source_ = NULL;
+}
+
+int AudioOutputController::Glue::OnMoreData(AudioBus* dest_bus,
+ AudioBuffersState buffers_state) {
+ return OnMoreIOData(NULL, dest_bus, buffers_state);
+}
+
+int AudioOutputController::Glue::OnMoreIOData(AudioBus* source_bus,
+ AudioBus* dest_bus,
+ AudioBuffersState buffers_state) {
TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData");
+ // Attempt to read from the SyncReader. While reading, do not hold lock_.
+ // After reading, acquire the lock again; and execute a delayed pass, if
+ // scheduled.
{
- // Check state and do nothing if we are not playing.
- // We are on the hardware audio thread, so lock is needed.
base::AutoLock auto_lock(lock_);
- if (state_ != kPlaying) {
- return 0;
+ if (source_ && controller_->state_ == kPlaying) {
+ is_reading_from_source_ = true;
+ int frames_read;
+ {
+ base::AutoUnlock unlock_while_reading(lock_);
+ frames_read = source_->Read(source_bus, dest_bus);
+ source_->UpdatePendingBytes(
+ buffers_state.total_bytes() +
+ frames_read * controller_->params_.GetBytesPerFrame());
+ }
+ is_reading_from_source_ = false;
+ if (!delayed_pass_.is_null()) {
+ delayed_pass_.Run();
+ delayed_pass_.Reset();
+ }
+ return frames_read;
}
}
- int frames = sync_reader_->Read(source, dest);
- sync_reader_->UpdatePendingBytes(
- buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
- return frames;
+ // We haven't received the SyncReader yet, so fill the destination with zeros.
+ dest_bus->Zero();
+ return dest_bus->frames();
}
-void AudioOutputController::WaitTillDataReady() {
+void AudioOutputController::Glue::WaitTillDataReady() {
#if defined(OS_WIN) || defined(OS_MACOSX)
base::Time start = base::Time::Now();
// Wait for up to 1.5 seconds for DataReady(). 1.5 seconds was chosen because
@@ -327,8 +399,10 @@ void AudioOutputController::WaitTillDataReady() {
// minimum supported sample rate: 4096 / 3000 = ~1.4 seconds. Even a client
// expecting real time playout should be able to fill in this time.
const base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(1500);
- while (!sync_reader_->DataReady() &&
+ base::AutoLock auto_lock(lock_);
+ while (source_ && !source_->DataReady() &&
((base::Time::Now() - start) < max_wait)) {
+ base::AutoUnlock unlock_while_yielding(lock_);
base::PlatformThread::YieldCurrentThread();
}
#else
@@ -337,10 +411,10 @@ void AudioOutputController::WaitTillDataReady() {
#endif
}
-void AudioOutputController::OnError(AudioOutputStream* stream, int code) {
+void AudioOutputController::Glue::OnError(AudioOutputStream* stream, int code) {
// Handle error on the audio controller thread.
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioOutputController::DoReportError, this, code));
+ controller_->message_loop_->PostTask(FROM_HERE, base::Bind(
+ &AudioOutputController::DoReportError, controller_, code));
}
void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) {
@@ -353,7 +427,6 @@ void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) {
stream_ = NULL;
audio_manager_->RemoveOutputDeviceChangeListener(this);
- audio_manager_ = NULL;
weak_this_.InvalidateWeakPtrs();
}
@@ -369,13 +442,11 @@ void AudioOutputController::OnDeviceChange() {
// We should always have a stream by this point.
CHECK(stream_);
- // Preserve the original state and shutdown the stream.
- State original_state = state_;
- stream_->Stop();
- stream_->Close();
- stream_ = NULL;
+ // Preserve the original state.
+ const State original_state = state_;
- // Recreate the stream, exit if we ran into an error.
+ // Recreate the stream (DoCreate() will first shut down an existing stream).
+ // Exit if we ran into an error.
state_ = kRecreating;
DoCreate();
if (!stream_ || state_ == kError)
@@ -385,6 +456,7 @@ void AudioOutputController::OnDeviceChange() {
switch (original_state) {
case kStarting:
case kPlaying:
+ DoSetVolume(volume_);
DoPlay();
return;
case kCreated:
@@ -397,4 +469,18 @@ void AudioOutputController::OnDeviceChange() {
}
}
+AudioOutputStream::AudioSourceCallback* AudioOutputController::Divert() {
+ DCHECK(!divert_glue_) << "BUG: Already diverted!";
+ divert_glue_ = new Glue(this, NULL);
+ stream_glue_->PassSoon(divert_glue_);
+ return divert_glue_;
+}
+
+void AudioOutputController::Revert(
+ AudioOutputStream::AudioSourceCallback* asc) {
+ DCHECK_EQ(divert_glue_, asc);
+ divert_glue_->PassSoon(stream_glue_);
+ divert_glue_ = NULL;
+}
+
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698