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

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: Moved Glue to cc file, and renamed to SourceSharingCallback. 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..68145bdfdf385f0d1ce432287c1d073af688231e 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -24,19 +24,76 @@ namespace media {
const int AudioOutputController::kPollNumAttempts = 3;
const int AudioOutputController::kPollPauseInMilliseconds = 3;
+// An AudioSourceCallback that allows passing around the underlying SyncReader
+// data source between consumers of the data. It does this in a "pass or
+// delayed pass" scheme that ensures synchronization between timing-sensitive
+// threads is effectively non-blocking. In addition, SourceSharingCallback
+// ensures that only one consumer is pulling data from the SyncReader at a time.
+class AudioOutputController::SourceSharingCallback
+ : public AudioOutputStream::AudioSourceCallback,
+ public base::RefCountedThreadSafe<SourceSharingCallback> {
+ public:
+ SourceSharingCallback(AudioOutputController* controller, SyncReader* source);
+
+ // Pass the SyncReader to another instance as soon as possible.
+ void PassSoon(const scoped_refptr<SourceSharingCallback>& receiver);
+
+ // AudioSourceCallback implementation.
+ virtual int OnMoreData(AudioBus* dest_bus,
+ AudioBuffersState buffers_state) OVERRIDE;
+ virtual int OnMoreIOData(AudioBus* source_bus,
+ AudioBus* dest_bus,
+ AudioBuffersState buffers_state) OVERRIDE;
+ virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE;
+ virtual void WaitTillDataReady() OVERRIDE;
+
+ private:
+ friend class base::RefCountedThreadSafe<SourceSharingCallback>;
+ virtual ~SourceSharingCallback();
+
+ // Take over reading from |source|.
+ void ReceiveSource(SyncReader* source);
+
+ // Executes a delayed pass (i.e., a pass that was delayed because source_ was
+ // in-use at the time PassSoon() was called).
+ void DoDelayedPassWithLockHeld(
+ const scoped_refptr<SourceSharingCallback>& to);
+
+ // Access to parent/owner instance.
+ AudioOutputController* const controller_;
+
+ // Guards passing of SyncReader to another SourceSharingCallback instance.
+ // The implementation should acquire lock_ for only very short operations
+ // since audio threads are timing-sensitive.
+ base::Lock lock_;
+
+ // Audio data source, or NULL if none yet.
+ SyncReader* source_;
+
+ // True while source_ is in-use without lock_ held.
+ bool is_reading_from_source_;
+
+ // When !delayed_pass_.is_null(), source_ is scheduled to be passed as soon as
+ // possible.
+ base::Closure delayed_pass_;
+
+ DISALLOW_COPY_AND_ASSIGN(SourceSharingCallback);
+};
+
AudioOutputController::AudioOutputController(AudioManager* audio_manager,
EventHandler* handler,
const AudioParameters& params,
SyncReader* sync_reader)
: audio_manager_(audio_manager),
+ params_(params),
handler_(handler),
stream_(NULL),
+ callback_for_stream_(new SourceSharingCallback(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 +127,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 +192,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 +269,7 @@ void AudioOutputController::StartStream() {
state_ = kPlaying;
// We start the AudioOutputStream lazily.
- stream_->Start(this);
+ stream_->Start(callback_for_stream_);
// Tell the event handler that we are now playing.
handler_->OnPlaying(this);
@@ -262,6 +316,7 @@ void AudioOutputController::DoClose() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ != kClosed) {
+ DCHECK(!callback_for_divert_);
DoStopCloseAndClearStream(NULL);
sync_reader_->Close();
state_ = kClosed;
@@ -294,32 +349,108 @@ void AudioOutputController::DoReportError(int code) {
handler_->OnError(this, code);
}
-int AudioOutputController::OnMoreData(AudioBus* dest,
- AudioBuffersState buffers_state) {
- return OnMoreIOData(NULL, dest, buffers_state);
+AudioOutputController::SourceSharingCallback::SourceSharingCallback(
+ AudioOutputController* controller, SyncReader* source)
+ : controller_(controller),
+ source_(source),
+ is_reading_from_source_(false) {
+}
+
+AudioOutputController::SourceSharingCallback::~SourceSharingCallback() {
+}
+
+void AudioOutputController::SourceSharingCallback::PassSoon(
+ const scoped_refptr<SourceSharingCallback>& to) {
+ 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(
+ &SourceSharingCallback::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::SourceSharingCallback::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::SourceSharingCallback::DoDelayedPassWithLockHeld(
+ const scoped_refptr<SourceSharingCallback>& to) {
+ lock_.AssertAcquired();
+
+ // Pass the SyncReader to another SourceSharingCallback instance, but do not
+ // allow the current thread to block on this action.
+ controller_->message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SourceSharingCallback::ReceiveSource, to, source_));
+ source_ = NULL;
+}
+
+int AudioOutputController::SourceSharingCallback::OnMoreData(
+ AudioBus* dest_bus, AudioBuffersState buffers_state) {
+ return OnMoreIOData(NULL, dest_bus, buffers_state);
+}
+
+int AudioOutputController::SourceSharingCallback::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::SourceSharingCallback::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 +458,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 +470,11 @@ void AudioOutputController::WaitTillDataReady() {
#endif
}
-void AudioOutputController::OnError(AudioOutputStream* stream, int code) {
+void AudioOutputController::SourceSharingCallback::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 +487,6 @@ void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent* done) {
stream_ = NULL;
audio_manager_->RemoveOutputDeviceChangeListener(this);
- audio_manager_ = NULL;
weak_this_.InvalidateWeakPtrs();
}
@@ -369,13 +502,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 +516,7 @@ void AudioOutputController::OnDeviceChange() {
switch (original_state) {
case kStarting:
case kPlaying:
+ DoSetVolume(volume_);
DoPlay();
return;
case kCreated:
@@ -397,4 +529,18 @@ void AudioOutputController::OnDeviceChange() {
}
}
+AudioOutputStream::AudioSourceCallback* AudioOutputController::Divert() {
+ DCHECK(!callback_for_divert_) << "BUG: Already diverted!";
+ callback_for_divert_ = new SourceSharingCallback(this, NULL);
+ callback_for_stream_->PassSoon(callback_for_divert_);
+ return callback_for_divert_;
+}
+
+void AudioOutputController::Revert(
+ AudioOutputStream::AudioSourceCallback* asc) {
+ DCHECK_EQ(callback_for_divert_, asc);
+ callback_for_divert_->PassSoon(callback_for_stream_);
+ callback_for_divert_ = NULL;
+}
+
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698