Index: media/filters/audio_renderer_impl_unittest.cc |
diff --git a/media/filters/audio_renderer_impl_unittest.cc b/media/filters/audio_renderer_impl_unittest.cc |
index 5adfbc499f7491f8176e71d4c801104f0fa1cf26..21bc3dcb34afc814a1a5999ec12ea5a0679b82c7 100644 |
--- a/media/filters/audio_renderer_impl_unittest.cc |
+++ b/media/filters/audio_renderer_impl_unittest.cc |
@@ -7,6 +7,7 @@ |
#include "base/gtest_prod_util.h" |
#include "base/memory/scoped_vector.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
#include "base/stl_util.h" |
#include "base/strings/stringprintf.h" |
#include "media/base/audio_buffer.h" |
@@ -46,7 +47,8 @@ class AudioRendererImplTest : public ::testing::Test { |
public: |
// Give the decoder some non-garbage media properties. |
AudioRendererImplTest() |
- : demuxer_stream_(DemuxerStream::AUDIO), |
+ : needs_stop_(true), |
+ demuxer_stream_(DemuxerStream::AUDIO), |
decoder_(new MockAudioDecoder()) { |
AudioDecoderConfig audio_config(kCodec, |
kSampleFormat, |
@@ -64,6 +66,9 @@ class AudioRendererImplTest : public ::testing::Test { |
EXPECT_CALL(*decoder_, Reset(_)) |
.WillRepeatedly(Invoke(this, &AudioRendererImplTest::ResetDecoder)); |
+ EXPECT_CALL(*decoder_, Stop(_)) |
+ .WillRepeatedly(Invoke(this, &AudioRendererImplTest::StopDecoder)); |
+ |
// Set up audio properties. |
EXPECT_CALL(*decoder_, bits_per_channel()) |
.WillRepeatedly(Return(audio_config.bits_per_channel())); |
@@ -88,9 +93,11 @@ class AudioRendererImplTest : public ::testing::Test { |
virtual ~AudioRendererImplTest() { |
SCOPED_TRACE("~AudioRendererImplTest()"); |
- WaitableMessageLoopEvent event; |
- renderer_->Stop(event.GetClosure()); |
- event.RunAndWait(); |
+ if (needs_stop_) { |
+ WaitableMessageLoopEvent event; |
+ renderer_->Stop(event.GetClosure()); |
+ event.RunAndWait(); |
+ } |
} |
void ExpectUnsupportedAudioDecoder() { |
@@ -371,11 +378,25 @@ class AudioRendererImplTest : public ::testing::Test { |
time_ += time; |
} |
+ void HoldStopDecoderCB() { |
+ EXPECT_CALL(*decoder_, Stop(_)).WillRepeatedly( |
+ Invoke(this, &AudioRendererImplTest::StopDecoderHoldCB)); |
+ } |
+ |
+ void DispatchHeldStopDecoderCB() { |
+ base::ResetAndReturn(&stop_decoder_cb_).Run(); |
+ } |
+ |
+ |
// Fixture members. |
base::MessageLoop message_loop_; |
scoped_ptr<AudioRendererImpl> renderer_; |
scoped_refptr<FakeAudioRendererSink> sink_; |
+ // Whether or not the test needs the destructor to call Stop() on |
+ // |renderer_| at destruction. |
+ bool needs_stop_; |
+ |
private: |
TimeTicks GetTime() { |
base::AutoLock auto_lock(lock_); |
@@ -383,6 +404,9 @@ class AudioRendererImplTest : public ::testing::Test { |
} |
void ReadDecoder(const AudioDecoder::ReadCB& read_cb) { |
+ // We shouldn't ever call Read() after Stop(): |
+ EXPECT_TRUE(stop_decoder_cb_.is_null()); |
+ |
// TODO(scherkus): Make this a DCHECK after threading semantics are fixed. |
if (base::MessageLoop::current() != &message_loop_) { |
message_loop_.PostTask(FROM_HERE, base::Bind( |
@@ -406,6 +430,14 @@ class AudioRendererImplTest : public ::testing::Test { |
message_loop_.PostTask(FROM_HERE, reset_cb); |
} |
+ void StopDecoder(const base::Closure& stop_cb) { |
+ message_loop_.PostTask(FROM_HERE, stop_cb); |
+ } |
+ |
+ void StopDecoderHoldCB(const base::Closure& stop_cb) { |
+ stop_decoder_cb_ = stop_cb; |
+ } |
+ |
void DeliverBuffer(AudioDecoder::Status status, |
const scoped_refptr<AudioBuffer>& buffer) { |
CHECK(!read_cb_.is_null()); |
@@ -428,6 +460,8 @@ class AudioRendererImplTest : public ::testing::Test { |
// Run during ReadDecoder() to unblock WaitForPendingRead(). |
base::Closure wait_for_pending_read_cb_; |
+ base::Closure stop_decoder_cb_; |
+ |
DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); |
}; |
@@ -769,7 +803,6 @@ TEST_F(AudioRendererImplTest, PendingRead_Pause) { |
Preroll(1000, PIPELINE_OK); |
} |
- |
TEST_F(AudioRendererImplTest, PendingRead_Flush) { |
Initialize(); |
@@ -798,7 +831,32 @@ TEST_F(AudioRendererImplTest, PendingRead_Flush) { |
Preroll(1000, PIPELINE_OK); |
} |
-TEST_F(AudioRendererImplTest, StopDuringFlush) { |
+TEST_F(AudioRendererImplTest, PendingRead_Stop) { |
+ Initialize(); |
+ |
+ Preroll(); |
+ Play(); |
+ |
+ // Partially drain internal buffer so we get a pending read. |
+ EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL)); |
+ WaitForPendingRead(); |
+ |
+ Pause(); |
+ |
+ EXPECT_TRUE(IsReadPending()); |
+ |
+ WaitableMessageLoopEvent stop_event; |
+ renderer_->Stop(stop_event.GetClosure()); |
+ needs_stop_ = false; |
+ |
+ SatisfyPendingRead(kDataSize); |
+ |
+ stop_event.RunAndWait(); |
+ |
+ EXPECT_FALSE(IsReadPending()); |
+} |
+ |
+TEST_F(AudioRendererImplTest, PendingFlush_Stop) { |
Initialize(); |
Preroll(); |
@@ -818,9 +876,40 @@ TEST_F(AudioRendererImplTest, StopDuringFlush) { |
SatisfyPendingRead(kDataSize); |
- // Request a Stop() before the flush completes. |
+ WaitableMessageLoopEvent event; |
+ renderer_->Stop(event.GetClosure()); |
+ event.RunAndWait(); |
+ needs_stop_ = false; |
+} |
+ |
+TEST_F(AudioRendererImplTest, PendingStop_Read) { |
+ // This reproduces crbug.com/335181, basically an extra Read() call to the |
+ // decoder can sneak into the gap between Stop() and its callback being run, |
+ // which is potentially problematic if we wait until that callback runs to |
+ // set the 'kStopped' state on the AudioRendererImpl. |
+ |
+ Initialize(); |
+ |
+ Preroll(); |
+ Play(); |
+ |
+ // Delay calling the callback passed to AudioDecoder::Stop() |
+ HoldStopDecoderCB(); |
+ |
+ // This will post at least one call to ARI::AttemptRead. |
+ ConsumeAllBufferedData(); |
+ |
WaitableMessageLoopEvent stop_event; |
renderer_->Stop(stop_event.GetClosure()); |
+ needs_stop_ = false; |
+ |
+ // Now, after stopping, but before the callback is run, let some pending |
+ // AttemptRead()'s go through... the AudioRendererImpl should ignore them, |
+ // since the Stop() should have put us into the kStopped state. |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ // Now let the stop callback run. |
+ DispatchHeldStopDecoderCB(); |
stop_event.RunAndWait(); |
} |