Commit 61346031 authored by Dale Curtis's avatar Dale Curtis Committed by Commit Bot

Purge AudioOutputDispatcherImpl's idle stream cache on device change.

Since default device is always matched as an empty string, we would
sometimes reconnect to a stream which was configured for the wrong
device.

R=guidou

Bug: 1091894
Change-Id: Ia3a33d8f91d984de5ceffbcbb4ddd96efed355e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2468302
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816749}
parent 778463de
......@@ -33,6 +33,7 @@ AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
&AudioOutputDispatcherImpl::CloseAllIdleStreams),
audio_stream_id_(0) {
DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread());
audio_manager->AddOutputDeviceChangeListener(this);
}
AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
......@@ -47,6 +48,8 @@ AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
// invalidating any outstanding tasks upon its destruction.
CloseAllIdleStreams();
audio_manager()->RemoveOutputDeviceChangeListener(this);
// All idle physical streams must have been closed during shutdown.
CHECK(idle_streams_.empty());
}
......@@ -134,6 +137,17 @@ void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
// StopStream().
void AudioOutputDispatcherImpl::FlushStream(AudioOutputProxy* stream_proxy) {}
void AudioOutputDispatcherImpl::OnDeviceChange() {
DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
// We don't want to end up reusing streams which were opened for the wrong
// default device. We need to post this task so it runs after device changes
// have been sent to all listeners and they've had time to close streams.
audio_manager()->GetTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&AudioOutputDispatcherImpl::CloseAllIdleStreams,
weak_factory_.GetWeakPtr()));
}
bool AudioOutputDispatcherImpl::HasOutputProxies() const {
DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
return idle_proxies_ || !proxy_to_physical_map_.empty();
......
......@@ -23,13 +23,16 @@
#include "base/macros.h"
#include "base/timer/timer.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_output_dispatcher.h"
#include "media/base/audio_parameters.h"
namespace media {
class AudioLog;
class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher {
class MEDIA_EXPORT AudioOutputDispatcherImpl
: public AudioOutputDispatcher,
public AudioManager::AudioDeviceListener {
public:
// |close_delay| specifies delay after the stream is idle until the audio
// device is closed.
......@@ -49,6 +52,9 @@ class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher {
void CloseStream(AudioOutputProxy* stream_proxy) override;
void FlushStream(AudioOutputProxy* stream_proxy) override;
// AudioDeviceListener implementation.
void OnDeviceChange() override;
// Returns true if there are any open AudioOutputProxy objects.
bool HasOutputProxies() const;
......
......@@ -640,6 +640,32 @@ TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_AfterStop) {
DispatcherDestroyed_AfterStop(std::move(resampler_));
}
TEST_F(AudioOutputProxyTest, DispatcherDeviceChangeClosesIdleStreams) {
// Set close delay so long that it triggers a test timeout if relied upon.
InitDispatcher(base::TimeDelta::FromSeconds(1000));
MockAudioOutputStream stream(&manager_, params_);
EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
.WillOnce(Return(&stream));
EXPECT_CALL(stream, Open()).WillOnce(Return(true));
AudioOutputProxy* proxy = dispatcher_impl_->CreateStreamProxy();
EXPECT_TRUE(proxy->Open());
// Close the stream and verify it doesn't happen immediately.
proxy->Close();
Mock::VerifyAndClear(&stream);
// This should trigger a true close on the stream.
dispatcher_impl_->OnDeviceChange();
base::RunLoop run_loop;
EXPECT_CALL(stream, Close())
.WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
run_loop.Run();
}
// Simulate AudioOutputStream::Create() failure with a low latency stream and
// ensure AudioOutputResampler falls back to the high latency path.
TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment