diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-10 21:44:38 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-10 21:44:38 +0000 |
commit | f72e58b294148fd1ec7d281dd06d1a16d49d4a55 (patch) | |
tree | 550ed5715d8375461687f1a2bcf97b1e3c43c79a | |
parent | 8d93614f9306e6b53455b469b8acf98613b4d417 (diff) | |
download | chromium_src-f72e58b294148fd1ec7d281dd06d1a16d49d4a55.zip chromium_src-f72e58b294148fd1ec7d281dd06d1a16d49d4a55.tar.gz chromium_src-f72e58b294148fd1ec7d281dd06d1a16d49d4a55.tar.bz2 |
Revert 282424 "Remove media::AudioRendererImpl::now_cb_ and clea..."
MSVC evaluates functions in a different order, causing test failures.
> Remove media::AudioRendererImpl::now_cb_ and clean up tests.
>
> It's no longer needed as of r282376.
>
> BUG=370634
> R=rileya@chromium.org
>
> Review URL: https://codereview.chromium.org/380893002
TBR=scherkus@chromium.org
Review URL: https://codereview.chromium.org/380303004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282436 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/filters/audio_renderer_impl.cc | 1 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.h | 9 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl_unittest.cc | 208 |
3 files changed, 161 insertions, 57 deletions
diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc index ae4096a..0436e9c 100644 --- a/media/filters/audio_renderer_impl.cc +++ b/media/filters/audio_renderer_impl.cc @@ -52,6 +52,7 @@ AudioRendererImpl::AudioRendererImpl( decoders.Pass(), set_decryptor_ready_cb), hardware_config_(hardware_config), + now_cb_(base::Bind(&base::TimeTicks::Now)), state_(kUninitialized), buffering_state_(BUFFERING_HAVE_NOTHING), rendering_(false), diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h index cc06fa3..2e29a9e 100644 --- a/media/filters/audio_renderer_impl.h +++ b/media/filters/audio_renderer_impl.h @@ -80,6 +80,12 @@ class MEDIA_EXPORT AudioRendererImpl virtual void StartPlayingFrom(base::TimeDelta timestamp) OVERRIDE; virtual void SetVolume(float volume) OVERRIDE; + // Allows injection of a custom time callback for non-realtime testing. + typedef base::Callback<base::TimeTicks()> NowCB; + void set_now_cb_for_testing(const NowCB& now_cb) { + now_cb_ = now_cb; + } + private: friend class AudioRendererImplTest; @@ -215,6 +221,9 @@ class MEDIA_EXPORT AudioRendererImpl // Callback provided to Flush(). base::Closure flush_cb_; + // Typically calls base::TimeTicks::Now() but can be overridden by a test. + NowCB now_cb_; + // After Initialize() has completed, all variables below must be accessed // under |lock_|. ------------------------------------------------------------ base::Lock lock_; diff --git a/media/filters/audio_renderer_impl_unittest.cc b/media/filters/audio_renderer_impl_unittest.cc index d6e048f..cb7626f 100644 --- a/media/filters/audio_renderer_impl_unittest.cc +++ b/media/filters/audio_renderer_impl_unittest.cc @@ -4,11 +4,17 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#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" #include "media/base/audio_buffer_converter.h" #include "media/base/audio_hardware_config.h" #include "media/base/audio_splicer.h" +#include "media/base/audio_timestamp_helper.h" #include "media/base/fake_audio_renderer_sink.h" #include "media/base/gmock_callback_support.h" #include "media/base/mock_filters.h" @@ -16,8 +22,12 @@ #include "media/filters/audio_renderer_impl.h" #include "testing/gtest/include/gtest/gtest.h" +using ::base::Time; +using ::base::TimeTicks; using ::base::TimeDelta; using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Invoke; using ::testing::Return; using ::testing::SaveArg; @@ -33,6 +43,11 @@ static int kSamplesPerSecond = 44100; // Use a different output sample rate so the AudioBufferConverter is invoked. static int kOutputSamplesPerSecond = 48000; +// Constants for distinguishing between muted audio and playing audio when using +// ConsumeBufferedData(). Must match the type needed by kSampleFormat. +static float kMutedAudio = 0.0f; +static float kPlayingAudio = 0.5f; + static const int kDataSize = 1024; ACTION_P(EnterPendingDecoderInitStateAction, test) { @@ -47,8 +62,7 @@ class AudioRendererImplTest : public ::testing::Test { needs_stop_(true), demuxer_stream_(DemuxerStream::AUDIO), decoder_(new MockAudioDecoder()), - last_time_update_(kNoTimestamp()), - ended_(false) { + last_time_update_(kNoTimestamp()) { AudioDecoderConfig audio_config(kCodec, kSampleFormat, kChannelLayout, @@ -84,6 +98,10 @@ class AudioRendererImplTest : public ::testing::Test { decoders.Pass(), SetDecryptorReadyCB(), &hardware_config_)); + + // Stub out time. + renderer_->set_now_cb_for_testing(base::Bind( + &AudioRendererImplTest::GetTime, base::Unretained(this))); } virtual ~AudioRendererImplTest() { @@ -120,8 +138,7 @@ class AudioRendererImplTest : public ::testing::Test { base::Unretained(this)), base::Bind(&AudioRendererImplTest::OnBufferingStateChange, base::Unretained(this)), - base::Bind(&AudioRendererImplTest::OnEnded, - base::Unretained(this)), + ended_event_.GetClosure(), base::Bind(&AudioRendererImplTest::OnError, base::Unretained(this))); } @@ -224,6 +241,11 @@ class AudioRendererImplTest : public ::testing::Test { renderer_->StopRendering(); } + void WaitForEnded() { + SCOPED_TRACE("WaitForEnded()"); + ended_event_.RunAndWait(); + } + bool IsReadPending() const { return !decode_cb_.is_null(); } @@ -253,7 +275,7 @@ class AudioRendererImplTest : public ::testing::Test { kChannelLayout, kChannelCount, kSamplesPerSecond, - 1.0f, + kPlayingAudio, 0.0f, size, next_timestamp_->GetTimestamp()); @@ -291,13 +313,46 @@ class AudioRendererImplTest : public ::testing::Test { } // Attempts to consume |requested_frames| frames from |renderer_|'s internal - // buffer, returning the actual number of frames consumed. - int ConsumeBufferedData(int requested_frames) { + // buffer, returning true if all |requested_frames| frames were consumed, + // false if less than |requested_frames| frames were consumed. + // + // |muted| is optional and if passed will get set if the value of + // the consumed data is muted audio. + bool ConsumeBufferedData(int requested_frames, bool* muted) { scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, std::max(requested_frames, 1)); + int frames_read; + if (!sink_->Render(bus.get(), 0, &frames_read)) { + if (muted) + *muted = true; + return false; + } + + if (muted) + *muted = frames_read < 1 || bus->channel(0)[0] == kMutedAudio; + return frames_read == requested_frames; + } + + // Attempts to consume all data available from the renderer. Returns the + // number of frames read. Since time is frozen, the audio delay will increase + // as frames come in. + int ConsumeAllBufferedData() { int frames_read = 0; - EXPECT_TRUE(sink_->Render(bus.get(), 0, &frames_read)); - return frames_read; + int total_frames_read = 0; + + scoped_ptr<AudioBus> bus = AudioBus::Create(kChannels, 1024); + + do { + TimeDelta audio_delay = TimeDelta::FromMicroseconds( + total_frames_read * Time::kMicrosecondsPerSecond / + static_cast<float>(hardware_config_.GetOutputConfig().sample_rate())); + + frames_read = renderer_->Render( + bus.get(), audio_delay.InMilliseconds()); + total_frames_read += frames_read; + } while (frames_read > 0); + + return total_frames_read; } int frames_buffered() { @@ -317,6 +372,55 @@ class AudioRendererImplTest : public ::testing::Test { return buffer_capacity() - frames_buffered(); } + TimeDelta CalculatePlayTime(int frames_filled) { + return TimeDelta::FromMicroseconds( + frames_filled * Time::kMicrosecondsPerSecond / + renderer_->audio_parameters_.sample_rate()); + } + + void EndOfStreamTest(float playback_rate) { + Initialize(); + Preroll(); + StartRendering(); + renderer_->SetPlaybackRate(playback_rate); + + // Drain internal buffer, we should have a pending read. + EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); + int total_frames = frames_buffered(); + int frames_filled = ConsumeAllBufferedData(); + WaitForPendingRead(); + + // Due to how the cross-fade algorithm works we won't get an exact match + // between the ideal and expected number of frames consumed. In the faster + // than normal playback case, more frames are created than should exist and + // vice versa in the slower than normal playback case. + const float kEpsilon = 0.20 * (total_frames / playback_rate); + EXPECT_NEAR(frames_filled, total_frames / playback_rate, kEpsilon); + + // Figure out how long until the ended event should fire. + TimeDelta audio_play_time = CalculatePlayTime(frames_filled); + DVLOG(1) << "audio_play_time = " << audio_play_time.InSecondsF(); + + // Fulfill the read with an end-of-stream packet. We shouldn't report ended + // nor have a read until we drain the internal buffer. + EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); + DeliverEndOfStream(); + + // Advance time half way without an ended expectation. + AdvanceTime(audio_play_time / 2); + ConsumeBufferedData(frames_buffered(), NULL); + + // Advance time by other half and expect the ended event. + AdvanceTime(audio_play_time / 2); + ConsumeBufferedData(frames_buffered(), NULL); + WaitForEnded(); + } + + void AdvanceTime(TimeDelta time) { + base::AutoLock auto_lock(lock_); + time_ += time; + } + void force_config_change() { renderer_->OnConfigChange(); } @@ -333,8 +437,6 @@ class AudioRendererImplTest : public ::testing::Test { return last_time_update_; } - bool ended() const { return ended_; } - // Fixture members. base::MessageLoop message_loop_; scoped_ptr<AudioRendererImpl> renderer_; @@ -346,6 +448,11 @@ class AudioRendererImplTest : public ::testing::Test { bool needs_stop_; private: + TimeTicks GetTime() { + base::AutoLock auto_lock(lock_); + return time_; + } + void DecodeDecoder(const scoped_refptr<DecoderBuffer>& buffer, const AudioDecoder::DecodeCB& decode_cb) { // We shouldn't ever call Read() after Stop(): @@ -391,27 +498,27 @@ class AudioRendererImplTest : public ::testing::Test { message_loop_.RunUntilIdle(); } - void OnEnded() { - CHECK(!ended_); - ended_ = true; - } - MockDemuxerStream demuxer_stream_; MockAudioDecoder* decoder_; + // Used for stubbing out time in the audio callback thread. + base::Lock lock_; + TimeTicks time_; + // Used for satisfying reads. AudioDecoder::OutputCB output_cb_; AudioDecoder::DecodeCB decode_cb_; base::Closure reset_cb_; scoped_ptr<AudioTimestampHelper> next_timestamp_; + WaitableMessageLoopEvent ended_event_; + // Run during DecodeDecoder() to unblock WaitForPendingRead(). base::Closure wait_for_pending_decode_cb_; base::Closure stop_decoder_cb_; PipelineStatusCB init_decoder_cb_; base::TimeDelta last_time_update_; - bool ended_; DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); }; @@ -436,35 +543,20 @@ TEST_F(AudioRendererImplTest, StartRendering) { StartRendering(); // Drain internal buffer, we should have a pending read. - EXPECT_EQ(frames_buffered(), ConsumeBufferedData(frames_buffered())); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL)); WaitForPendingRead(); } TEST_F(AudioRendererImplTest, EndOfStream) { - Initialize(); - Preroll(); - StartRendering(); - - // Drain internal buffer, we should have a pending read. - EXPECT_EQ(frames_buffered(), ConsumeBufferedData(frames_buffered())); - WaitForPendingRead(); - - // Forcefully trigger underflow. - EXPECT_EQ(0, ConsumeBufferedData(1)); - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); - - // Fulfill the read with an end-of-stream buffer. Doing so should change our - // buffering state so playback resumes. - EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); - DeliverEndOfStream(); + EndOfStreamTest(1.0); +} - // Consume all remaining data. We shouldn't have signal ended yet. - EXPECT_EQ(frames_buffered(), ConsumeBufferedData(frames_buffered())); - EXPECT_FALSE(ended()); +TEST_F(AudioRendererImplTest, EndOfStream_FasterPlaybackSpeed) { + EndOfStreamTest(2.0); +} - // Ended should trigger on next render call. - EXPECT_EQ(0, ConsumeBufferedData(1)); - EXPECT_TRUE(ended()); +TEST_F(AudioRendererImplTest, EndOfStream_SlowerPlaybackSpeed) { + EndOfStreamTest(0.5); } TEST_F(AudioRendererImplTest, Underflow) { @@ -473,24 +565,27 @@ TEST_F(AudioRendererImplTest, Underflow) { StartRendering(); // Drain internal buffer, we should have a pending read. - EXPECT_EQ(frames_buffered(), ConsumeBufferedData(frames_buffered())); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL)); WaitForPendingRead(); // Verify the next FillBuffer() call triggers a buffering state change // update. EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); - EXPECT_EQ(0, ConsumeBufferedData(kDataSize)); + EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL)); // Verify we're still not getting audio data. + bool muted = false; EXPECT_EQ(0, frames_buffered()); - EXPECT_EQ(0, ConsumeBufferedData(kDataSize)); + EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted)); + EXPECT_TRUE(muted); // Deliver enough data to have enough for buffering. EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); DeliverRemainingAudio(); // Verify we're getting audio data. - EXPECT_EQ(kDataSize, ConsumeBufferedData(kDataSize)); + EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted)); + EXPECT_FALSE(muted); } TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) { @@ -499,14 +594,14 @@ TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) { StartRendering(); // Drain internal buffer, we should have a pending read. - EXPECT_EQ(frames_buffered(), ConsumeBufferedData(frames_buffered())); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL)); WaitForPendingRead(); // Verify the next FillBuffer() call triggers the underflow callback // since the decoder hasn't delivered any data after it was drained. int initial_capacity = buffer_capacity(); EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); - EXPECT_EQ(0, ConsumeBufferedData(kDataSize)); + EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL)); // Verify that the buffer capacity increased as a result of underflowing. EXPECT_GT(buffer_capacity(), initial_capacity); @@ -522,10 +617,10 @@ TEST_F(AudioRendererImplTest, Underflow_Flush) { StartRendering(); // Force underflow. - EXPECT_EQ(frames_buffered(), ConsumeBufferedData(frames_buffered())); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL)); WaitForPendingRead(); EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); - EXPECT_EQ(0, ConsumeBufferedData(kDataSize)); + EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL)); WaitForPendingRead(); StopRendering(); @@ -540,7 +635,7 @@ TEST_F(AudioRendererImplTest, PendingRead_Flush) { StartRendering(); // Partially drain internal buffer so we get a pending read. - EXPECT_EQ(frames_buffered() / 2, ConsumeBufferedData(frames_buffered() / 2)); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL)); WaitForPendingRead(); StopRendering(); @@ -562,7 +657,7 @@ TEST_F(AudioRendererImplTest, PendingRead_Stop) { StartRendering(); // Partially drain internal buffer so we get a pending read. - EXPECT_EQ(frames_buffered() / 2, ConsumeBufferedData(frames_buffered() / 2)); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL)); WaitForPendingRead(); StopRendering(); @@ -587,7 +682,7 @@ TEST_F(AudioRendererImplTest, PendingFlush_Stop) { StartRendering(); // Partially drain internal buffer so we get a pending read. - EXPECT_EQ(frames_buffered() / 2, ConsumeBufferedData(frames_buffered() / 2)); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered() / 2, NULL)); WaitForPendingRead(); StopRendering(); @@ -621,7 +716,7 @@ TEST_F(AudioRendererImplTest, ConfigChangeDrainsConverter) { StartRendering(); // Drain internal buffer, we should have a pending read. - EXPECT_EQ(frames_buffered(), ConsumeBufferedData(frames_buffered())); + EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL)); WaitForPendingRead(); // Deliver a little bit of data. Use an odd data size to ensure there is data @@ -647,7 +742,7 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) { // Preroll() should be buffered some data, consume half of it now. int frames_to_consume = frames_buffered() / 2; - EXPECT_EQ(frames_to_consume, ConsumeBufferedData(frames_to_consume)); + EXPECT_TRUE(ConsumeBufferedData(frames_to_consume, NULL)); WaitForPendingRead(); base::RunLoop().RunUntilIdle(); @@ -660,7 +755,7 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) { // The next time update should match the remaining frames_buffered(), but only // after running the message loop. frames_to_consume = frames_buffered(); - EXPECT_EQ(frames_to_consume, ConsumeBufferedData(frames_to_consume)); + EXPECT_TRUE(ConsumeBufferedData(frames_to_consume, NULL)); EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update()); base::RunLoop().RunUntilIdle(); @@ -680,9 +775,8 @@ TEST_F(AudioRendererImplTest, ImmediateEndOfStream) { StartRendering(); // Read a single frame. We shouldn't be able to satisfy it. - EXPECT_FALSE(ended()); - EXPECT_EQ(0, ConsumeBufferedData(1)); - EXPECT_TRUE(ended()); + EXPECT_FALSE(ConsumeBufferedData(1, NULL)); + WaitForEnded(); } TEST_F(AudioRendererImplTest, OnRenderErrorCausesDecodeError) { |