summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-10 21:44:38 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-10 21:44:38 +0000
commitf72e58b294148fd1ec7d281dd06d1a16d49d4a55 (patch)
tree550ed5715d8375461687f1a2bcf97b1e3c43c79a /media
parent8d93614f9306e6b53455b469b8acf98613b4d417 (diff)
downloadchromium_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
Diffstat (limited to 'media')
-rw-r--r--media/filters/audio_renderer_impl.cc1
-rw-r--r--media/filters/audio_renderer_impl.h9
-rw-r--r--media/filters/audio_renderer_impl_unittest.cc208
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) {