summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authordalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-14 23:59:15 +0000
committerdalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-14 23:59:15 +0000
commit270c55bdb11c8f07f48ddf9f95a200d5a0855361 (patch)
treeff0f5d0efd02a136c3d6867dad680242bf39bf25 /media
parent4a5ddd2054ab917b0535764e2027999636a33e71 (diff)
downloadchromium_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.cc27
-rw-r--r--media/audio/fake_audio_output_stream.h4
-rw-r--r--media/audio/fake_audio_output_stream_unittest.cc53
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