summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-04 00:45:47 +0000
committerdalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-04 00:45:47 +0000
commit2f8c23b482763d6c4060dc007389cfd6adc7490e (patch)
tree8ed7cb9d3da482df2af5eb4bfeaabcc539294155
parenteded43ded82a05127b0d2bdc4d388be1e3510719 (diff)
downloadchromium_src-2f8c23b482763d6c4060dc007389cfd6adc7490e.zip
chromium_src-2f8c23b482763d6c4060dc007389cfd6adc7490e.tar.gz
chromium_src-2f8c23b482763d6c4060dc007389cfd6adc7490e.tar.bz2
Attempt to fix audio wedges by restarting all streams on OSX.
Introduces two new methods to AudioOutputDispatcher: CloseStreamsForWedgeFix() and RestartStreamsForWedgeFix(). Respectively, each method closes or restarts all active streams owned by a given dispatcher. The process is completely transparent to upstream clients. A new method on AudioManager, FixWedgedAudio() calls CloseStreamsForWedgeFix() for all dispatchers and then calls RestartStreamsForWedgeFix() afterward. FixWedgedAudio() is called by each AudioOutputController when a wedge is detected. Multiple in flight wedge checks are serialized by the audio thread. The hope is that wedges will be fixed before the next WedgeCheck() fires. While the methods are available on all platforms, FixWedgedAudio() is only wired up on OSX. BUG=160920 TEST=unittest. fake wedge and observe stream recreation. R=scherkus@chromium.org Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=238325 Review URL: https://codereview.chromium.org/61203008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238501 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/audio/audio_manager.h5
-rw-r--r--media/audio/audio_manager_base.cc28
-rw-r--r--media/audio/audio_manager_base.h2
-rw-r--r--media/audio/audio_output_controller.cc11
-rw-r--r--media/audio/audio_output_dispatcher.h7
-rw-r--r--media/audio/audio_output_dispatcher_impl.cc19
-rw-r--r--media/audio/audio_output_dispatcher_impl.h3
-rw-r--r--media/audio/audio_output_proxy_unittest.cc60
-rw-r--r--media/audio/audio_output_resampler.cc24
-rw-r--r--media/audio/audio_output_resampler.h2
-rw-r--r--media/audio/mock_audio_manager.cc2
-rw-r--r--media/audio/mock_audio_manager.h2
12 files changed, 163 insertions, 2 deletions
diff --git a/media/audio/audio_manager.h b/media/audio/audio_manager.h
index ab9c82b..a6cb705d 100644
--- a/media/audio/audio_manager.h
+++ b/media/audio/audio_manager.h
@@ -175,6 +175,11 @@ class MEDIA_EXPORT AudioManager {
virtual std::string GetAssociatedOutputDeviceID(
const std::string& input_device_id) = 0;
+ // Called when a component has detected a OS level audio wedge. Shuts down
+ // all active audio streams and then restarts them transparently. See
+ // http://crbug.com/160920
+ virtual void FixWedgedAudio() = 0;
+
protected:
AudioManager();
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc
index 52d545e..fc6574c 100644
--- a/media/audio/audio_manager_base.cc
+++ b/media/audio/audio_manager_base.cc
@@ -418,4 +418,32 @@ int AudioManagerBase::GetUserBufferSize() {
return 0;
}
+void AudioManagerBase::FixWedgedAudio() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+#if defined(OS_MACOSX)
+ // Through trial and error, we've found that one way to restore audio after a
+ // hang is to close all outstanding audio streams. Once all streams have been
+ // closed, new streams appear to work correctly.
+ //
+ // In Chrome terms, this means we need to ask all AudioOutputDispatchers to
+ // close all Open()'d streams. Once all streams across all dispatchers have
+ // been closed, we ask for all previously Start()'d streams to be recreated
+ // using the same AudioSourceCallback they had before.
+ //
+ // Since this operation takes place on the audio thread we can be sure that no
+ // other state-changing stream operations will take place while the fix is in
+ // progress.
+ //
+ // See http://crbug.com/160920 for additional details.
+ for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin();
+ it != output_dispatchers_.end(); ++it) {
+ (*it)->dispatcher->CloseStreamsForWedgeFix();
+ }
+ for (AudioOutputDispatchers::iterator it = output_dispatchers_.begin();
+ it != output_dispatchers_.end(); ++it) {
+ (*it)->dispatcher->RestartStreamsForWedgeFix();
+ }
+#endif
+}
+
} // namespace media
diff --git a/media/audio/audio_manager_base.h b/media/audio/audio_manager_base.h
index 860ed5c..2fe71b7 100644
--- a/media/audio/audio_manager_base.h
+++ b/media/audio/audio_manager_base.h
@@ -115,6 +115,8 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager {
virtual std::string GetAssociatedOutputDeviceID(
const std::string& input_device_id) OVERRIDE;
+ virtual void FixWedgedAudio() OVERRIDE;
+
protected:
AudioManagerBase();
diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc
index 994e8f4..92f9f25 100644
--- a/media/audio/audio_output_controller.cc
+++ b/media/audio/audio_output_controller.cc
@@ -478,8 +478,15 @@ void AudioOutputController::WedgeCheck() {
// If we should be playing and we haven't, that's a wedge.
if (state_ == kPlaying) {
- UMA_HISTOGRAM_BOOLEAN("Media.AudioOutputControllerPlaybackStartupSuccess",
- base::AtomicRefCountIsOne(&on_more_io_data_called_));
+ const bool playback_success =
+ base::AtomicRefCountIsOne(&on_more_io_data_called_);
+
+ UMA_HISTOGRAM_BOOLEAN(
+ "Media.AudioOutputControllerPlaybackStartupSuccess", playback_success);
+
+ // Let the AudioManager try and fix it.
+ if (!playback_success)
+ audio_manager_->FixWedgedAudio();
}
}
diff --git a/media/audio/audio_output_dispatcher.h b/media/audio/audio_output_dispatcher.h
index 5004fa0..ac69634 100644
--- a/media/audio/audio_output_dispatcher.h
+++ b/media/audio/audio_output_dispatcher.h
@@ -66,6 +66,13 @@ class MEDIA_EXPORT AudioOutputDispatcher
// Called on the audio thread when the AudioManager is shutting down.
virtual void Shutdown() = 0;
+ // Called by the AudioManager to restart streams when a wedge is detected. A
+ // wedge means the OS failed to request any audio after StartStream(). When a
+ // wedge is detected all streams across all dispatchers must be closed. After
+ // all streams are closed, streams are restarted. See http://crbug.com/160920
+ virtual void CloseStreamsForWedgeFix() = 0;
+ virtual void RestartStreamsForWedgeFix() = 0;
+
// Accessor to the input device id used by unified IO.
const std::string& input_device_id() const { return input_device_id_; }
diff --git a/media/audio/audio_output_dispatcher_impl.cc b/media/audio/audio_output_dispatcher_impl.cc
index eaac8a4..92c7da4 100644
--- a/media/audio/audio_output_dispatcher_impl.cc
+++ b/media/audio/audio_output_dispatcher_impl.cc
@@ -59,6 +59,8 @@ bool AudioOutputDispatcherImpl::StartStream(
AudioOutputStream::AudioSourceCallback* callback,
AudioOutputProxy* stream_proxy) {
DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(proxy_to_physical_map_.find(stream_proxy) ==
+ proxy_to_physical_map_.end());
if (idle_streams_.empty() && !CreateAndOpenStream())
return false;
@@ -186,4 +188,21 @@ void AudioOutputDispatcherImpl::ClosePendingStreams() {
}
}
+void AudioOutputDispatcherImpl::CloseStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ while (!pausing_streams_.empty()) {
+ idle_streams_.push_back(pausing_streams_.back());
+ pausing_streams_.pop_back();
+ }
+ ClosePendingStreams();
+}
+
+void AudioOutputDispatcherImpl::RestartStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ // Should only be called when the dispatcher is used with fake streams which
+ // don't need to be shutdown or restarted.
+ CHECK_EQ(params_.format(), AudioParameters::AUDIO_FAKE);
+}
+
} // namespace media
diff --git a/media/audio/audio_output_dispatcher_impl.h b/media/audio/audio_output_dispatcher_impl.h
index f17f3a6..ca30b3a 100644
--- a/media/audio/audio_output_dispatcher_impl.h
+++ b/media/audio/audio_output_dispatcher_impl.h
@@ -59,6 +59,9 @@ class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher {
virtual void Shutdown() OVERRIDE;
+ virtual void CloseStreamsForWedgeFix() OVERRIDE;
+ virtual void RestartStreamsForWedgeFix() OVERRIDE;
+
private:
typedef std::map<AudioOutputProxy*, AudioOutputStream*> AudioStreamMap;
friend class base::RefCountedThreadSafe<AudioOutputDispatcherImpl>;
diff --git a/media/audio/audio_output_proxy_unittest.cc b/media/audio/audio_output_proxy_unittest.cc
index 3cffcc9..74e93eb 100644
--- a/media/audio/audio_output_proxy_unittest.cc
+++ b/media/audio/audio_output_proxy_unittest.cc
@@ -748,4 +748,64 @@ TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) {
EXPECT_FALSE(stream3.start_called());
}
+// Ensures the methods used to fix audio output wedges are working correctly.
+TEST_F(AudioOutputResamplerTest, WedgeFix) {
+ MockAudioOutputStream stream1(&manager_, params_);
+ MockAudioOutputStream stream2(&manager_, params_);
+ MockAudioOutputStream stream3(&manager_, params_);
+
+ // Setup the mock such that all three streams are successfully created.
+ EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+ .WillOnce(Return(&stream1))
+ .WillOnce(Return(&stream2))
+ .WillOnce(Return(&stream3));
+
+ // Stream1 should be able to successfully open and start.
+ EXPECT_CALL(stream1, Open())
+ .WillOnce(Return(true));
+ EXPECT_CALL(stream1, Close());
+ EXPECT_CALL(stream1, SetVolume(_));
+ EXPECT_CALL(stream2, Open())
+ .WillOnce(Return(true));
+ EXPECT_CALL(stream2, Close());
+
+ // Open and start the first proxy and stream.
+ AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_.get());
+ EXPECT_TRUE(proxy1->Open());
+ proxy1->Start(&callback_);
+ OnStart();
+
+ // Open, but do not start the second proxy.
+ AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_.get());
+ EXPECT_TRUE(proxy2->Open());
+
+ // Wait for stream to timeout and shutdown.
+ WaitForCloseTimer(kTestCloseDelayMs);
+
+ resampler_->CloseStreamsForWedgeFix();
+
+ // Stream3 should take Stream1's place after RestartStreamsForWedgeFix().
+ EXPECT_CALL(stream3, Open())
+ .WillOnce(Return(true));
+ EXPECT_CALL(stream3, Close());
+ EXPECT_CALL(stream3, SetVolume(_));
+
+ resampler_->RestartStreamsForWedgeFix();
+ OnStart();
+
+ // Perform the required Stop()/Close() shutdown dance for each proxy.
+ proxy2->Close();
+ proxy1->Stop();
+ proxy1->Close();
+
+ // Wait for all of the messages to fly and then verify stream behavior.
+ WaitForCloseTimer(kTestCloseDelayMs);
+ EXPECT_TRUE(stream1.stop_called());
+ EXPECT_TRUE(stream1.start_called());
+ EXPECT_FALSE(stream2.stop_called());
+ EXPECT_FALSE(stream2.start_called());
+ EXPECT_TRUE(stream3.stop_called());
+ EXPECT_TRUE(stream3.start_called());
+}
+
} // namespace media
diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc
index 86b40dd..3179ddb 100644
--- a/media/audio/audio_output_resampler.cc
+++ b/media/audio/audio_output_resampler.cc
@@ -291,6 +291,30 @@ void AudioOutputResampler::Shutdown() {
DCHECK(callbacks_.empty());
}
+void AudioOutputResampler::CloseStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ // Stop and close all active streams. Once all streams across all dispatchers
+ // have been closed the AudioManager will call RestartStreamsForWedgeFix().
+ for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
+ ++it) {
+ dispatcher_->StopStream(it->first);
+ dispatcher_->CloseStream(it->first);
+ }
+
+ // Close all idle streams as well.
+ dispatcher_->CloseStreamsForWedgeFix();
+}
+
+void AudioOutputResampler::RestartStreamsForWedgeFix() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
+ ++it) {
+ dispatcher_->OpenStream();
+ dispatcher_->StartStream(it->second, it->first);
+ }
+}
+
OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
const AudioParameters& output_params)
: io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
diff --git a/media/audio/audio_output_resampler.h b/media/audio/audio_output_resampler.h
index 297d4e3..a8fca23 100644
--- a/media/audio/audio_output_resampler.h
+++ b/media/audio/audio_output_resampler.h
@@ -53,6 +53,8 @@ class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher {
double volume) OVERRIDE;
virtual void CloseStream(AudioOutputProxy* stream_proxy) OVERRIDE;
virtual void Shutdown() OVERRIDE;
+ virtual void CloseStreamsForWedgeFix() OVERRIDE;
+ virtual void RestartStreamsForWedgeFix() OVERRIDE;
private:
friend class base::RefCountedThreadSafe<AudioOutputResampler>;
diff --git a/media/audio/mock_audio_manager.cc b/media/audio/mock_audio_manager.cc
index 6b54be2..31a0a4d 100644
--- a/media/audio/mock_audio_manager.cc
+++ b/media/audio/mock_audio_manager.cc
@@ -103,4 +103,6 @@ std::string MockAudioManager::GetAssociatedOutputDeviceID(
return std::string();
}
+void MockAudioManager::FixWedgedAudio() {}
+
} // namespace media.
diff --git a/media/audio/mock_audio_manager.h b/media/audio/mock_audio_manager.h
index 6b2b4c3..48f1d79 100644
--- a/media/audio/mock_audio_manager.h
+++ b/media/audio/mock_audio_manager.h
@@ -67,6 +67,8 @@ class MockAudioManager : public media::AudioManager {
virtual std::string GetAssociatedOutputDeviceID(
const std::string& input_device_id) OVERRIDE;
+ virtual void FixWedgedAudio() OVERRIDE;
+
protected:
virtual ~MockAudioManager();