diff options
author | dalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-14 23:59:15 +0000 |
---|---|---|
committer | dalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-14 23:59:15 +0000 |
commit | 270c55bdb11c8f07f48ddf9f95a200d5a0855361 (patch) | |
tree | ff0f5d0efd02a136c3d6867dad680242bf39bf25 /media | |
parent | 4a5ddd2054ab917b0535764e2027999636a33e71 (diff) | |
download | chromium_src-270c55bdb11c8f07f48ddf9f95a200d5a0855361.zip chromium_src-270c55bdb11c8f07f48ddf9f95a200d5a0855361.tar.gz chromium_src-270c55bdb11c8f07f48ddf9f95a200d5a0855361.tar.bz2 |
Fix fake audio output stream to callback on a regular basis.
The precision in milliseconds wasn't enough for higher sample rates, 48000
and 44100 both end up with 2ms sleeps vs the correct 2.6ms and 2.8ms
respectively. I've also changed the code to dynamically adjust the timing for
better consistency like we do in VirtualAudioInputStream.
BUG=176192
TEST=fake audio plays back at the correct speed.
Review URL: https://codereview.chromium.org/12254024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182580 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-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 |