diff options
-rw-r--r-- | media/audio/fake_audio_output_stream.cc | 27 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream.h | 4 | ||||
-rw-r--r-- | media/audio/fake_audio_output_stream_unittest.cc | 53 |
3 files changed, 50 insertions, 34 deletions
diff --git a/media/audio/fake_audio_output_stream.cc b/media/audio/fake_audio_output_stream.cc index c21026d..8db548c 100644 --- a/media/audio/fake_audio_output_stream.cc +++ b/media/audio/fake_audio_output_stream.cc @@ -23,9 +23,10 @@ FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager, : audio_manager_(manager), callback_(NULL), audio_bus_(AudioBus::Create(params)), - frames_per_millisecond_( - params.sample_rate() / static_cast<float>( - base::Time::kMillisecondsPerSecond)) { + buffer_duration_(base::TimeDelta::FromMicroseconds( + params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / + static_cast<float>(params.sample_rate()))) { + audio_bus_->Zero(); } FakeAudioOutputStream::~FakeAudioOutputStream() { @@ -40,6 +41,7 @@ bool FakeAudioOutputStream::Open() { void FakeAudioOutputStream::Start(AudioSourceCallback* callback) { DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); callback_ = callback; + next_read_time_ = base::Time::Now(); on_more_data_cb_.Reset(base::Bind( &FakeAudioOutputStream::OnMoreDataTask, base::Unretained(this))); audio_manager_->GetMessageLoop()->PostTask( @@ -68,15 +70,20 @@ void FakeAudioOutputStream::OnMoreDataTask() { DCHECK(audio_manager_->GetMessageLoop()->BelongsToCurrentThread()); DCHECK(callback_); - audio_bus_->Zero(); - int frames_received = callback_->OnMoreData( - audio_bus_.get(), AudioBuffersState()); + callback_->OnMoreData(audio_bus_.get(), AudioBuffersState()); + + // Need to account for time spent here due to the cost of OnMoreData() as well + // as the imprecision of PostDelayedTask(). + base::Time now = base::Time::Now(); + base::TimeDelta delay = next_read_time_ + buffer_duration_ - now; + + // If we're behind, find the next nearest ontime interval. + if (delay < base::TimeDelta()) + delay += buffer_duration_ * (-delay / buffer_duration_ + 1); + next_read_time_ = now + delay; - // Calculate our sleep duration for simulated playback. Sleep for at least - // one millisecond so we don't spin the CPU. audio_manager_->GetMessageLoop()->PostDelayedTask( - FROM_HERE, on_more_data_cb_.callback(), base::TimeDelta::FromMilliseconds( - std::max(1.0f, frames_received / frames_per_millisecond_))); + FROM_HERE, on_more_data_cb_.callback(), delay); } } // namespace media diff --git a/media/audio/fake_audio_output_stream.h b/media/audio/fake_audio_output_stream.h index d188b9f..ec3a592 100644 --- a/media/audio/fake_audio_output_stream.h +++ b/media/audio/fake_audio_output_stream.h @@ -7,6 +7,7 @@ #include "base/cancelable_callback.h" #include "base/memory/scoped_ptr.h" +#include "base/time.h" #include "media/audio/audio_io.h" #include "media/audio/audio_parameters.h" @@ -42,7 +43,8 @@ class MEDIA_EXPORT FakeAudioOutputStream : public AudioOutputStream { AudioManagerBase* audio_manager_; AudioSourceCallback* callback_; scoped_ptr<AudioBus> audio_bus_; - float frames_per_millisecond_; + base::TimeDelta buffer_duration_; + base::Time next_read_time_; // Used to post delayed tasks to the AudioThread that we can cancel. base::CancelableClosure on_more_data_cb_; diff --git a/media/audio/fake_audio_output_stream_unittest.cc b/media/audio/fake_audio_output_stream_unittest.cc index 6838e3f..8b37b86 100644 --- a/media/audio/fake_audio_output_stream_unittest.cc +++ b/media/audio/fake_audio_output_stream_unittest.cc @@ -13,19 +13,21 @@ namespace media { +static const int kTestCallbacks = 5; + class FakeAudioOutputStreamTest : public testing::Test { public: FakeAudioOutputStreamTest() : audio_manager_(AudioManager::Create()), params_( - AudioParameters::AUDIO_FAKE, CHANNEL_LAYOUT_STEREO, 8000, 8, 128), + AudioParameters::AUDIO_FAKE, CHANNEL_LAYOUT_STEREO, 44100, 8, 128), source_(params_.channels(), 200.0, params_.sample_rate()), done_(false, false) { stream_ = audio_manager_->MakeAudioOutputStream(AudioParameters(params_)); CHECK(stream_); - time_between_callbacks_ = base::TimeDelta::FromMilliseconds( - params_.frames_per_buffer() * base::Time::kMillisecondsPerSecond / + time_between_callbacks_ = base::TimeDelta::FromMicroseconds( + params_.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / static_cast<float>(params_.sample_rate())); } @@ -64,11 +66,10 @@ class FakeAudioOutputStreamTest : public testing::Test { if (source_.callbacks() < callbacks) { audio_manager_->GetMessageLoop()->PostDelayedTask(FROM_HERE, base::Bind( &FakeAudioOutputStreamTest::TimeCallbacksOnAudioThread, - base::Unretained(this), callbacks), time_between_callbacks_); + base::Unretained(this), callbacks), time_between_callbacks_ / 2); } else { - audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( - &FakeAudioOutputStreamTest::EndTest, base::Unretained(this), - callbacks)); + end_time_ = base::Time::Now(); + EndTest(callbacks); } } @@ -88,6 +89,7 @@ class FakeAudioOutputStreamTest : public testing::Test { SineWaveAudioSource source_; base::WaitableEvent done_; base::Time start_time_; + base::Time end_time_; base::TimeDelta time_between_callbacks_; private: @@ -105,36 +107,41 @@ TEST_F(FakeAudioOutputStreamTest, FakeStreamBasicCallback) { // Ensure the time between callbacks is sane. TEST_F(FakeAudioOutputStreamTest, TimeBetweenCallbacks) { - static const int kTestCallbacks = 5; - audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( &FakeAudioOutputStreamTest::TimeCallbacksOnAudioThread, base::Unretained(this), kTestCallbacks)); - // Let the loop run for a second or two then issue Stop() / Start(). - audio_manager_->GetMessageLoop()->PostDelayedTask(FROM_HERE, base::Bind( - &FakeAudioOutputStreamTest::StopStartOnAudioThread, - base::Unretained(this)), time_between_callbacks_); - done_.Wait(); - base::TimeDelta elapsed = base::Time::Now() - start_time_; - // There are only (kTestCallbacks - 1) intervals between kTestCallbacks. - float actual_time_between_callbacks_ms = - elapsed.InMillisecondsF() / (kTestCallbacks - 1); - float expected_time_between_callbacks_ms = - time_between_callbacks_.InMillisecondsF(); + base::TimeDelta actual_time_between_callbacks = + (end_time_ - start_time_) / (kTestCallbacks - 1); // Ensure callback time is no faster than the expected time between callbacks. - EXPECT_GE(actual_time_between_callbacks_ms, - expected_time_between_callbacks_ms); + EXPECT_TRUE(actual_time_between_callbacks >= time_between_callbacks_); // Softly check if the callback time is no slower than twice the expected time // between callbacks. Since this test runs on the bots we can't be too strict // with the bounds. - if (actual_time_between_callbacks_ms > 2 * expected_time_between_callbacks_ms) + if (actual_time_between_callbacks > 2 * time_between_callbacks_) LOG(ERROR) << "Time between fake audio callbacks is too large!"; } +// Ensure Start()/Stop() on the stream doesn't generate too many callbacks. See +// http://crbug.com/159049 +TEST_F(FakeAudioOutputStreamTest, StartStopClearsCallbacks) { + audio_manager_->GetMessageLoop()->PostTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::TimeCallbacksOnAudioThread, + base::Unretained(this), kTestCallbacks)); + + // Issue a Stop() / Start() in between expected callbacks to maximize the + // chance of catching the FakeAudioOutputStream doing the wrong thing. + audio_manager_->GetMessageLoop()->PostDelayedTask(FROM_HERE, base::Bind( + &FakeAudioOutputStreamTest::StopStartOnAudioThread, + base::Unretained(this)), time_between_callbacks_ / 2); + + // EndTest() will ensure the proper number of callbacks have occurred. + done_.Wait(); +} + } // namespace media |