Commit 5b6ce11b authored by scherkus@chromium.org's avatar scherkus@chromium.org

Make media::AudioClock track frames written to compute time.

Instead of using AudioRendererAlgorithm's timestamp as the ending
timestamp and counting "backwards", count "forwards" from a starting
timestamp to compute the current media time. Doing so produces more
accurate time calculations during periods where the playback rate
is changing.

last_endpoint_timestamp() is replaced by a new method that computes
the amount of contiguous media data buffered by audio hardware. Using
this value gives a more accurate maximum time value to use when doing
linear interpolation.

BUG=367343,370634

Review URL: https://codereview.chromium.org/436053002

Cr-Commit-Position: refs/heads/master@{#288445}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288445 0039d316-1c4b-4281-b951-d872f2087c98
parent aed12363
...@@ -4,144 +4,137 @@ ...@@ -4,144 +4,137 @@
#include "media/filters/audio_clock.h" #include "media/filters/audio_clock.h"
#include <algorithm>
#include "base/logging.h" #include "base/logging.h"
#include "media/base/buffers.h" #include "media/base/buffers.h"
namespace media { namespace media {
AudioClock::AudioClock(int sample_rate) AudioClock::AudioClock(base::TimeDelta start_timestamp, int sample_rate)
: sample_rate_(sample_rate), last_endpoint_timestamp_(kNoTimestamp()) { : start_timestamp_(start_timestamp),
sample_rate_(sample_rate),
microseconds_per_frame_(
static_cast<double>(base::Time::kMicrosecondsPerSecond) /
sample_rate),
total_buffered_frames_(0),
current_media_timestamp_(start_timestamp),
audio_data_buffered_(0) {
} }
AudioClock::~AudioClock() { AudioClock::~AudioClock() {
} }
void AudioClock::WroteAudio(int frames, void AudioClock::WroteAudio(int frames_written,
int frames_requested,
int delay_frames, int delay_frames,
float playback_rate, float playback_rate) {
base::TimeDelta timestamp) { DCHECK_GE(frames_written, 0);
CHECK_GT(playback_rate, 0); DCHECK_LE(frames_written, frames_requested);
CHECK(timestamp != kNoTimestamp());
DCHECK_GE(frames, 0);
DCHECK_GE(delay_frames, 0); DCHECK_GE(delay_frames, 0);
DCHECK_GE(playback_rate, 0);
// First write: initialize buffer with silence.
if (start_timestamp_ == current_media_timestamp_ && buffered_.empty())
PushBufferedAudioData(delay_frames, 0.0f);
// Move frames from |buffered_| into the computed timestamp based on
// |delay_frames|.
//
// The ordering of compute -> push -> pop eliminates unnecessary memory
// reallocations in cases where |buffered_| gets emptied.
int64_t frames_played =
std::max(INT64_C(0), total_buffered_frames_ - delay_frames);
current_media_timestamp_ += ComputeBufferedMediaTime(frames_played);
PushBufferedAudioData(frames_written, playback_rate);
PushBufferedAudioData(frames_requested - frames_written, 0.0f);
PopBufferedAudioData(frames_played);
// Update cached values.
double scaled_frames = 0;
double scaled_frames_at_same_rate = 0;
bool found_silence = false;
audio_data_buffered_ = false;
for (size_t i = 0; i < buffered_.size(); ++i) {
if (buffered_[i].playback_rate == 0) {
found_silence = true;
continue;
}
if (last_endpoint_timestamp_ == kNoTimestamp()) audio_data_buffered_ = true;
PushBufferedAudio(delay_frames, 0, kNoTimestamp());
TrimBufferedAudioToMatchDelay(delay_frames);
PushBufferedAudio(frames, playback_rate, timestamp);
last_endpoint_timestamp_ = timestamp; // Any buffered silence breaks our contiguous stretch of audio data.
} if (found_silence)
break;
void AudioClock::WroteSilence(int frames, int delay_frames) { scaled_frames += (buffered_[i].frames * buffered_[i].playback_rate);
DCHECK_GE(frames, 0);
DCHECK_GE(delay_frames, 0);
if (last_endpoint_timestamp_ == kNoTimestamp()) if (i == 0)
PushBufferedAudio(delay_frames, 0, kNoTimestamp()); scaled_frames_at_same_rate = scaled_frames;
}
TrimBufferedAudioToMatchDelay(delay_frames); contiguous_audio_data_buffered_ = base::TimeDelta::FromMicroseconds(
PushBufferedAudio(frames, 0, kNoTimestamp()); scaled_frames * microseconds_per_frame_);
contiguous_audio_data_buffered_at_same_rate_ =
base::TimeDelta::FromMicroseconds(scaled_frames_at_same_rate *
microseconds_per_frame_);
} }
base::TimeDelta AudioClock::CurrentMediaTimestamp( base::TimeDelta AudioClock::CurrentMediaTimestampSinceWriting(
base::TimeDelta time_since_writing) const { base::TimeDelta time_since_writing) const {
int frames_to_skip = int64_t frames_played_since_writing = std::min(
static_cast<int>(time_since_writing.InSecondsF() * sample_rate_); total_buffered_frames_,
int silence_frames = 0; static_cast<int64_t>(time_since_writing.InSecondsF() * sample_rate_));
for (size_t i = 0; i < buffered_audio_.size(); ++i) { return current_media_timestamp_ +
int frames = buffered_audio_[i].frames; ComputeBufferedMediaTime(frames_played_since_writing);
if (frames_to_skip > 0) { }
if (frames <= frames_to_skip) {
frames_to_skip -= frames;
continue;
}
frames -= frames_to_skip;
frames_to_skip = 0;
}
// Account for silence ahead of the buffer closest to being played.
if (buffered_audio_[i].playback_rate == 0) {
silence_frames += frames;
continue;
}
// Multiply by playback rate as frames represent time-scaled audio.
return buffered_audio_[i].endpoint_timestamp -
base::TimeDelta::FromMicroseconds(
((frames * buffered_audio_[i].playback_rate) + silence_frames) /
sample_rate_ * base::Time::kMicrosecondsPerSecond);
}
// Either: AudioClock::AudioData::AudioData(int64_t frames, float playback_rate)
// 1) AudioClock is uninitialziated and we'll return kNoTimestamp() : frames(frames), playback_rate(playback_rate) {
// 2) All previously buffered audio has been replaced by silence,
// meaning media time is now at the last endpoint
return last_endpoint_timestamp_;
} }
void AudioClock::TrimBufferedAudioToMatchDelay(int delay_frames) { void AudioClock::PushBufferedAudioData(int64_t frames, float playback_rate) {
if (buffered_audio_.empty()) if (frames == 0)
return; return;
size_t i = buffered_audio_.size() - 1; total_buffered_frames_ += frames;
while (true) {
if (buffered_audio_[i].frames <= delay_frames) {
// Reached the end before accounting for all of |delay_frames|. This
// means we haven't written enough audio data yet to account for hardware
// delay. In this case, do nothing.
if (i == 0)
return;
// Keep accounting for |delay_frames|.
delay_frames -= buffered_audio_[i].frames;
--i;
continue;
}
// All of |delay_frames| has been accounted for: adjust amount of frames // Avoid creating extra elements where possible.
// left in current buffer. All preceeding elements with index < |i| should if (!buffered_.empty() && buffered_.back().playback_rate == playback_rate) {
// be considered played out and hence discarded. buffered_.back().frames += frames;
buffered_audio_[i].frames = delay_frames; return;
break;
} }
// At this point |i| points at what will be the new head of |buffered_audio_| buffered_.push_back(AudioData(frames, playback_rate));
// however if it contains no audio it should be removed as well.
if (buffered_audio_[i].frames == 0)
++i;
buffered_audio_.erase(buffered_audio_.begin(), buffered_audio_.begin() + i);
} }
void AudioClock::PushBufferedAudio(int frames, void AudioClock::PopBufferedAudioData(int64_t frames) {
float playback_rate, DCHECK_LE(frames, total_buffered_frames_);
base::TimeDelta endpoint_timestamp) {
if (playback_rate == 0)
DCHECK(endpoint_timestamp == kNoTimestamp());
if (frames == 0) total_buffered_frames_ -= frames;
return;
// Avoid creating extra elements where possible. while (frames > 0) {
if (!buffered_audio_.empty() && int64_t frames_to_pop = std::min(buffered_.front().frames, frames);
buffered_audio_.back().playback_rate == playback_rate) { buffered_.front().frames -= frames_to_pop;
buffered_audio_.back().frames += frames; if (buffered_.front().frames == 0)
buffered_audio_.back().endpoint_timestamp = endpoint_timestamp; buffered_.pop_front();
return;
}
buffered_audio_.push_back( frames -= frames_to_pop;
BufferedAudio(frames, playback_rate, endpoint_timestamp)); }
} }
AudioClock::BufferedAudio::BufferedAudio(int frames, base::TimeDelta AudioClock::ComputeBufferedMediaTime(int64_t frames) const {
float playback_rate, DCHECK_LE(frames, total_buffered_frames_);
base::TimeDelta endpoint_timestamp)
: frames(frames), double scaled_frames = 0;
playback_rate(playback_rate), for (size_t i = 0; i < buffered_.size() && frames > 0; ++i) {
endpoint_timestamp(endpoint_timestamp) { int64_t min_frames = std::min(buffered_[i].frames, frames);
scaled_frames += min_frames * buffered_[i].playback_rate;
frames -= min_frames;
}
return base::TimeDelta::FromMicroseconds(scaled_frames *
microseconds_per_frame_);
} }
} // namespace media } // namespace media
...@@ -18,59 +18,75 @@ namespace media { ...@@ -18,59 +18,75 @@ namespace media {
// a playback pipeline with large delay. // a playback pipeline with large delay.
class MEDIA_EXPORT AudioClock { class MEDIA_EXPORT AudioClock {
public: public:
explicit AudioClock(int sample_rate); AudioClock(base::TimeDelta start_timestamp, int sample_rate);
~AudioClock(); ~AudioClock();
// |frames| amount of audio data scaled to |playback_rate| was written. // |frames_written| amount of audio data scaled to |playback_rate| written.
// |frames_requested| amount of audio data requested by hardware.
// |delay_frames| is the current amount of hardware delay. // |delay_frames| is the current amount of hardware delay.
// |timestamp| is the endpoint media timestamp of the audio data written. void WroteAudio(int frames_written,
void WroteAudio(int frames, int frames_requested,
int delay_frames, int delay_frames,
float playback_rate, float playback_rate);
base::TimeDelta timestamp);
// |frames| amount of silence was written.
// |delay_frames| is the current amount of hardware delay.
void WroteSilence(int frames, int delay_frames);
// Calculates the current media timestamp taking silence and changes in // Calculates the current media timestamp taking silence and changes in
// playback rate into account. // playback rate into account.
// base::TimeDelta current_media_timestamp() const {
return current_media_timestamp_;
}
// Clients can provide |time_since_writing| to simulate the passage of time // Clients can provide |time_since_writing| to simulate the passage of time
// since last writing audio to get a more accurate current media timestamp. // since last writing audio to get a more accurate current media timestamp.
base::TimeDelta CurrentMediaTimestamp( base::TimeDelta CurrentMediaTimestampSinceWriting(
base::TimeDelta time_since_writing) const; base::TimeDelta time_since_writing) const;
// Returns the last endpoint timestamp provided to WroteAudio(). // Returns the amount of contiguous media time buffered at the head of the
base::TimeDelta last_endpoint_timestamp() const { // audio hardware buffer. Silence introduced into the audio hardware buffer is
return last_endpoint_timestamp_; // treated as a break in media time.
base::TimeDelta contiguous_audio_data_buffered() const {
return contiguous_audio_data_buffered_;
} }
private: // Same as above, but also treats changes in playback rate as a break in media
void TrimBufferedAudioToMatchDelay(int delay_frames); // time.
void PushBufferedAudio(int frames, base::TimeDelta contiguous_audio_data_buffered_at_same_rate() const {
float playback_rate, return contiguous_audio_data_buffered_at_same_rate_;
base::TimeDelta endpoint_timestamp); }
const int sample_rate_;
// Initially set to kNoTimestamp(), otherwise is the last endpoint timestamp // Returns true if there is any audio data buffered by the audio hardware,
// delivered to WroteAudio(). A copy is kept outside of |buffered_audio_| to // even if there is silence mixed in.
// handle the case where all of |buffered_audio_| has been replaced with bool audio_data_buffered() const { return audio_data_buffered_; }
// silence.
base::TimeDelta last_endpoint_timestamp_;
struct BufferedAudio { private:
BufferedAudio(int frames, // Even with a ridiculously high sample rate of 256kHz, using 64 bits will
float playback_rate, // permit tracking up to 416999965 days worth of time (that's 1141 millenia).
base::TimeDelta endpoint_timestamp); //
// 32 bits on the other hand would top out at measly 2 hours and 20 minutes.
struct AudioData {
AudioData(int64_t frames, float playback_rate);
int frames; int64_t frames;
float playback_rate; float playback_rate;
base::TimeDelta endpoint_timestamp;
}; };
std::deque<BufferedAudio> buffered_audio_; // Helpers for operating on |buffered_|.
void PushBufferedAudioData(int64_t frames, float playback_rate);
void PopBufferedAudioData(int64_t frames);
base::TimeDelta ComputeBufferedMediaTime(int64_t frames) const;
const base::TimeDelta start_timestamp_;
const int sample_rate_;
const double microseconds_per_frame_;
std::deque<AudioData> buffered_;
int64_t total_buffered_frames_;
base::TimeDelta current_media_timestamp_;
// Cached results of last call to WroteAudio().
bool audio_data_buffered_;
base::TimeDelta contiguous_audio_data_buffered_;
base::TimeDelta contiguous_audio_data_buffered_at_same_rate_;
DISALLOW_COPY_AND_ASSIGN(AudioClock); DISALLOW_COPY_AND_ASSIGN(AudioClock);
}; };
......
...@@ -12,171 +12,242 @@ namespace media { ...@@ -12,171 +12,242 @@ namespace media {
class AudioClockTest : public testing::Test { class AudioClockTest : public testing::Test {
public: public:
AudioClockTest() AudioClockTest()
: sample_rate_(10), : sample_rate_(10), clock_(base::TimeDelta(), sample_rate_) {}
timestamp_helper_(sample_rate_),
clock_(sample_rate_) {
timestamp_helper_.SetBaseTimestamp(base::TimeDelta());
}
virtual ~AudioClockTest() {} virtual ~AudioClockTest() {}
void WroteAudio(int frames, int delay_frames, float playback_rate) { void WroteAudio(int frames_written,
timestamp_helper_.AddFrames(static_cast<int>(frames * playback_rate)); int frames_requested,
int delay_frames,
float playback_rate) {
clock_.WroteAudio( clock_.WroteAudio(
frames, delay_frames, playback_rate, timestamp_helper_.GetTimestamp()); frames_written, frames_requested, delay_frames, playback_rate);
} }
void WroteSilence(int frames, int delay_frames) { int CurrentMediaTimestampInDays() {
clock_.WroteSilence(frames, delay_frames); return clock_.current_media_timestamp().InDays();
} }
int CurrentMediaTimestampInMilliseconds() { int CurrentMediaTimestampInMilliseconds() {
return CurrentMediaTimestampSinceLastWritingInMilliseconds(0); return clock_.current_media_timestamp().InMilliseconds();
} }
int CurrentMediaTimestampSinceLastWritingInMilliseconds(int milliseconds) { int CurrentMediaTimestampSinceLastWritingInMilliseconds(int milliseconds) {
return clock_.CurrentMediaTimestamp(base::TimeDelta::FromMilliseconds( return clock_.CurrentMediaTimestampSinceWriting(
milliseconds)).InMilliseconds(); base::TimeDelta::FromMilliseconds(milliseconds))
.InMilliseconds();
}
int ContiguousAudioDataBufferedInDays() {
return clock_.contiguous_audio_data_buffered().InDays();
}
int ContiguousAudioDataBufferedInMilliseconds() {
return clock_.contiguous_audio_data_buffered().InMilliseconds();
} }
int LastEndpointTimestampInMilliseconds() { int ContiguousAudioDataBufferedAtSameRateInMilliseconds() {
return clock_.last_endpoint_timestamp().InMilliseconds(); return clock_.contiguous_audio_data_buffered_at_same_rate()
.InMilliseconds();
} }
const int sample_rate_; const int sample_rate_;
AudioTimestampHelper timestamp_helper_;
AudioClock clock_; AudioClock clock_;
private: private:
DISALLOW_COPY_AND_ASSIGN(AudioClockTest); DISALLOW_COPY_AND_ASSIGN(AudioClockTest);
}; };
TEST_F(AudioClockTest, TimestampsStartAtNoTimestamp) { TEST_F(AudioClockTest, CurrentMediaTimestampStartsAtStartTimestamp) {
EXPECT_EQ(kNoTimestamp(), clock_.CurrentMediaTimestamp(base::TimeDelta())); base::TimeDelta expected = base::TimeDelta::FromSeconds(123);
EXPECT_EQ(kNoTimestamp(), clock_.last_endpoint_timestamp()); AudioClock clock(expected, sample_rate_);
EXPECT_EQ(expected, clock.current_media_timestamp());
}
TEST_F(AudioClockTest,
CurrentMediaTimestampSinceWritingStartsAtStartTimestamp) {
base::TimeDelta expected = base::TimeDelta::FromSeconds(123);
AudioClock clock(expected, sample_rate_);
base::TimeDelta time_since_writing = base::TimeDelta::FromSeconds(456);
EXPECT_EQ(expected,
clock.CurrentMediaTimestampSinceWriting(time_since_writing));
}
TEST_F(AudioClockTest, ContiguousAudioDataBufferedStartsAtZero) {
EXPECT_EQ(base::TimeDelta(), clock_.contiguous_audio_data_buffered());
}
TEST_F(AudioClockTest, ContiguousAudioDataBufferedAtSameRateStartsAtZero) {
EXPECT_EQ(base::TimeDelta(),
clock_.contiguous_audio_data_buffered_at_same_rate());
}
TEST_F(AudioClockTest, AudioDataBufferedStartsAtFalse) {
EXPECT_FALSE(clock_.audio_data_buffered());
} }
TEST_F(AudioClockTest, Playback) { TEST_F(AudioClockTest, Playback) {
// The first time we write data we should expect a negative time matching the // The first time we write data we should still expect our start timestamp
// current delay. // due to delay.
WroteAudio(10, 20, 1.0); WroteAudio(10, 10, 20, 1.0);
EXPECT_EQ(-2000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(1000, LastEndpointTimestampInMilliseconds());
// The media time should keep advancing as we write data.
WroteAudio(10, 20, 1.0);
EXPECT_EQ(-1000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(2000, LastEndpointTimestampInMilliseconds());
WroteAudio(10, 20, 1.0);
EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
EXPECT_TRUE(clock_.audio_data_buffered());
WroteAudio(10, 20, 1.0); // The media time should remain at start timestamp as we write data.
WroteAudio(10, 10, 20, 1.0);
EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(10, 10, 20, 1.0);
EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
// The media time should now start advanced now that delay has been covered.
WroteAudio(10, 10, 20, 1.0);
EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(4000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
// Introduce a rate change to slow down time. Current time will keep advancing WroteAudio(10, 10, 20, 1.0);
// by one second until it hits the slowed down audio.
WroteAudio(10, 20, 0.5);
EXPECT_EQ(2000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(2000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(4500, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(10, 20, 0.5); // Introduce a rate change to slow down time:
// - Current time will advance by one second until it hits rate change
// - Contiguous audio data will start shrinking immediately
WroteAudio(10, 10, 20, 0.5);
EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(5000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(2500, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(2000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(10, 20, 0.5); WroteAudio(10, 10, 20, 0.5);
EXPECT_EQ(4000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(4000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(5500, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(2000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(1000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(10, 20, 0.5);
EXPECT_EQ(4500, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(6000, LastEndpointTimestampInMilliseconds());
// Introduce a rate change to speed up time. Current time will keep advancing WroteAudio(10, 10, 20, 0.5);
// by half a second until it hits the the sped up audio.
WroteAudio(10, 20, 2);
EXPECT_EQ(5000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(5000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(8000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(1500, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(1500, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(10, 20, 2); WroteAudio(10, 10, 20, 0.5);
EXPECT_EQ(5500, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(5500, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(10000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(1500, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(1500, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(10, 20, 2); // Introduce a rate change to speed up time:
// - Current time will advance by half a second until it hits rate change
// - Contiguous audio data will start growing immediately
WroteAudio(10, 10, 20, 2);
EXPECT_EQ(6000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(6000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(12000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(1000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(10, 20, 2);
EXPECT_EQ(8000, CurrentMediaTimestampInMilliseconds()); WroteAudio(10, 10, 20, 2);
EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(6500, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(4500, ContiguousAudioDataBufferedInMilliseconds());
// Write silence to simulate reaching end of stream. EXPECT_EQ(500, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteSilence(10, 20);
EXPECT_EQ(10000, CurrentMediaTimestampInMilliseconds()); WroteAudio(10, 10, 20, 2);
EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(7000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(6000, ContiguousAudioDataBufferedInMilliseconds());
WroteSilence(10, 20); EXPECT_EQ(6000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
EXPECT_EQ(12000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); WroteAudio(10, 10, 20, 2);
EXPECT_EQ(9000, CurrentMediaTimestampInMilliseconds());
WroteSilence(10, 20); EXPECT_EQ(6000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(14000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(6000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds());
// Write silence to simulate reaching end of stream:
// - Current time will advance by half a second until it hits silence
// - Contiguous audio data will start shrinking towards zero
WroteAudio(0, 10, 20, 2);
EXPECT_EQ(11000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(4000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(4000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
WroteAudio(0, 10, 20, 2);
EXPECT_EQ(13000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(2000, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(2000, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
EXPECT_TRUE(clock_.audio_data_buffered()); // Still audio data buffered.
WroteAudio(0, 10, 20, 2);
EXPECT_EQ(15000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
EXPECT_FALSE(clock_.audio_data_buffered()); // No more audio data buffered.
// At this point media time should stop increasing. // At this point media time should stop increasing.
WroteSilence(10, 20); WroteAudio(0, 10, 20, 2);
EXPECT_EQ(14000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(15000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds());
EXPECT_FALSE(clock_.audio_data_buffered());
} }
TEST_F(AudioClockTest, AlternatingAudioAndSilence) { TEST_F(AudioClockTest, AlternatingAudioAndSilence) {
// Buffer #1: [0, 1000) // Buffer #1: [0, 1000)
WroteAudio(10, 20, 1.0); WroteAudio(10, 10, 20, 1.0);
EXPECT_EQ(-2000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
// Buffer #2: 1000ms of silence // Buffer #2: 1000ms of silence
WroteSilence(10, 20); WroteAudio(0, 10, 20, 1.0);
EXPECT_EQ(-1000, CurrentMediaTimestampInMilliseconds());
// Buffer #3: [1000, 2000), buffer #1 is at front
WroteAudio(10, 20, 1.0);
EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
// Buffer #4: 1000ms of silence, time shouldn't advance // Buffer #3: [1000, 2000):
WroteSilence(10, 20); // - Buffer #1 is at front with 1000ms of contiguous audio data
WroteAudio(10, 10, 20, 1.0);
EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds());
// Buffer #4: 1000ms of silence
// - Buffer #1 has been played out
// - Buffer #2 of silence leaves us with 0ms of contiguous audio data
WroteAudio(0, 10, 20, 1.0);
EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
// Buffer #5: [2000, 3000), buffer #3 is at front // Buffer #5: [2000, 3000):
WroteAudio(10, 20, 1.0); // - Buffer #3 is at front with 1000ms of contiguous audio data
WroteAudio(10, 10, 20, 1.0);
EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds());
} }
TEST_F(AudioClockTest, ZeroDelay) { TEST_F(AudioClockTest, ZeroDelay) {
// The first time we write data we should expect the first timestamp // The first time we write data we should expect the first timestamp
// immediately. // immediately.
WroteAudio(10, 0, 1.0); WroteAudio(10, 10, 0, 1.0);
EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(1000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds());
// Ditto for all subsequent buffers. // Ditto for all subsequent buffers.
WroteAudio(10, 0, 1.0); WroteAudio(10, 10, 0, 1.0);
EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(2000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds());
WroteAudio(10, 0, 1.0); WroteAudio(10, 10, 0, 1.0);
EXPECT_EQ(2000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(2000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds());
// Ditto for silence. // Ditto for silence.
WroteSilence(10, 0); WroteAudio(0, 10, 0, 1.0);
EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
WroteSilence(10, 0); WroteAudio(0, 10, 0, 1.0);
EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
} }
TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) { TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) {
...@@ -187,20 +258,20 @@ TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) { ...@@ -187,20 +258,20 @@ TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) {
// +-------------------+----------------+------------------+----------------+ // +-------------------+----------------+------------------+----------------+
// Media timestamp: 0 1000 1500 3500 // Media timestamp: 0 1000 1500 3500
// Wall clock time: 2000 3000 4000 5000 // Wall clock time: 2000 3000 4000 5000
WroteAudio(10, 40, 1.0); WroteAudio(10, 10, 40, 1.0);
WroteAudio(10, 40, 0.5); WroteAudio(10, 10, 40, 0.5);
WroteAudio(10, 40, 2.0); WroteAudio(10, 10, 40, 2.0);
EXPECT_EQ(-2000, CurrentMediaTimestampInMilliseconds()); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds());
EXPECT_EQ(3500, LastEndpointTimestampInMilliseconds()); EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds());
// Simulate passing 2000ms of initial delay in the audio hardware. // Simulate passing 2000ms of initial delay in the audio hardware.
EXPECT_EQ(-2000, CurrentMediaTimestampSinceLastWritingInMilliseconds(0)); EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(0));
EXPECT_EQ(-1500, CurrentMediaTimestampSinceLastWritingInMilliseconds(500)); EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(500));
EXPECT_EQ(-1000, CurrentMediaTimestampSinceLastWritingInMilliseconds(1000)); EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(1000));
EXPECT_EQ(-500, CurrentMediaTimestampSinceLastWritingInMilliseconds(1500)); EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(1500));
EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(2000)); EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(2000));
// New we should see the 1.0x buffer. // Now we should see the 1.0x buffer.
EXPECT_EQ(500, CurrentMediaTimestampSinceLastWritingInMilliseconds(2500)); EXPECT_EQ(500, CurrentMediaTimestampSinceLastWritingInMilliseconds(2500));
EXPECT_EQ(1000, CurrentMediaTimestampSinceLastWritingInMilliseconds(3000)); EXPECT_EQ(1000, CurrentMediaTimestampSinceLastWritingInMilliseconds(3000));
...@@ -213,11 +284,40 @@ TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) { ...@@ -213,11 +284,40 @@ TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) {
EXPECT_EQ(3500, CurrentMediaTimestampSinceLastWritingInMilliseconds(5000)); EXPECT_EQ(3500, CurrentMediaTimestampSinceLastWritingInMilliseconds(5000));
// Times beyond the known length of the audio clock should return the last // Times beyond the known length of the audio clock should return the last
// value we know of. // media timestamp we know of.
EXPECT_EQ(LastEndpointTimestampInMilliseconds(), EXPECT_EQ(3500, CurrentMediaTimestampSinceLastWritingInMilliseconds(5001));
CurrentMediaTimestampSinceLastWritingInMilliseconds(5001)); EXPECT_EQ(3500, CurrentMediaTimestampSinceLastWritingInMilliseconds(6000));
EXPECT_EQ(LastEndpointTimestampInMilliseconds(), }
CurrentMediaTimestampSinceLastWritingInMilliseconds(6000));
TEST_F(AudioClockTest, SupportsYearsWorthOfAudioData) {
// Use number of frames that would be likely to overflow 32-bit integer math.
const int huge_amount_of_frames = std::numeric_limits<int>::max();
const base::TimeDelta huge =
base::TimeDelta::FromSeconds(huge_amount_of_frames / sample_rate_);
EXPECT_EQ(2485, huge.InDays()); // Just to give some context on how big...
// Use zero delay to test calculation of current timestamp.
WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0);
EXPECT_EQ(0, CurrentMediaTimestampInDays());
EXPECT_EQ(2485, ContiguousAudioDataBufferedInDays());
WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0);
EXPECT_EQ(huge.InDays(), CurrentMediaTimestampInDays());
EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays());
WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0);
EXPECT_EQ((huge * 2).InDays(), CurrentMediaTimestampInDays());
EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays());
WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0);
EXPECT_EQ((huge * 3).InDays(), CurrentMediaTimestampInDays());
EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays());
// Use huge delay to test calculation of buffered data.
WroteAudio(
huge_amount_of_frames, huge_amount_of_frames, huge_amount_of_frames, 1.0);
EXPECT_EQ((huge * 3).InDays(), CurrentMediaTimestampInDays());
EXPECT_EQ((huge * 2).InDays(), ContiguousAudioDataBufferedInDays());
} }
} // namespace media } // namespace media
...@@ -61,6 +61,7 @@ AudioRendererImpl::AudioRendererImpl( ...@@ -61,6 +61,7 @@ AudioRendererImpl::AudioRendererImpl(
pending_read_(false), pending_read_(false),
received_end_of_stream_(false), received_end_of_stream_(false),
rendered_end_of_stream_(false), rendered_end_of_stream_(false),
last_timestamp_update_(kNoTimestamp()),
weak_factory_(this) { weak_factory_(this) {
audio_buffer_stream_->set_splice_observer(base::Bind( audio_buffer_stream_->set_splice_observer(base::Bind(
&AudioRendererImpl::OnNewSpliceBuffer, weak_factory_.GetWeakPtr())); &AudioRendererImpl::OnNewSpliceBuffer, weak_factory_.GetWeakPtr()));
...@@ -147,6 +148,7 @@ void AudioRendererImpl::SetMediaTime(base::TimeDelta time) { ...@@ -147,6 +148,7 @@ void AudioRendererImpl::SetMediaTime(base::TimeDelta time) {
DCHECK_EQ(state_, kFlushed); DCHECK_EQ(state_, kFlushed);
start_timestamp_ = time; start_timestamp_ = time;
audio_clock_.reset(new AudioClock(time, audio_parameters_.sample_rate()));
} }
base::TimeDelta AudioRendererImpl::CurrentMediaTime() { base::TimeDelta AudioRendererImpl::CurrentMediaTime() {
...@@ -201,9 +203,10 @@ void AudioRendererImpl::ResetDecoderDone() { ...@@ -201,9 +203,10 @@ void AudioRendererImpl::ResetDecoderDone() {
DCHECK_EQ(state_, kFlushed); DCHECK_EQ(state_, kFlushed);
DCHECK(!flush_cb_.is_null()); DCHECK(!flush_cb_.is_null());
audio_clock_.reset(new AudioClock(audio_parameters_.sample_rate())); audio_clock_.reset();
received_end_of_stream_ = false; received_end_of_stream_ = false;
rendered_end_of_stream_ = false; rendered_end_of_stream_ = false;
last_timestamp_update_ = kNoTimestamp();
// Flush() may have been called while underflowed/not fully buffered. // Flush() may have been called while underflowed/not fully buffered.
if (buffering_state_ != BUFFERING_HAVE_NOTHING) if (buffering_state_ != BUFFERING_HAVE_NOTHING)
...@@ -294,7 +297,8 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, ...@@ -294,7 +297,8 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream,
hardware_config_->GetHighLatencyBufferSize()); hardware_config_->GetHighLatencyBufferSize());
} }
audio_clock_.reset(new AudioClock(audio_parameters_.sample_rate())); audio_clock_.reset(
new AudioClock(base::TimeDelta(), audio_parameters_.sample_rate()));
audio_buffer_stream_->Initialize( audio_buffer_stream_->Initialize(
stream, stream,
...@@ -549,18 +553,21 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, ...@@ -549,18 +553,21 @@ int AudioRendererImpl::Render(AudioBus* audio_bus,
// Ensure Stop() hasn't destroyed our |algorithm_| on the pipeline thread. // Ensure Stop() hasn't destroyed our |algorithm_| on the pipeline thread.
if (!algorithm_) { if (!algorithm_) {
audio_clock_->WroteSilence(requested_frames, delay_frames); audio_clock_->WroteAudio(
0, requested_frames, delay_frames, playback_rate_);
return 0; return 0;
} }
if (playback_rate_ == 0) { if (playback_rate_ == 0) {
audio_clock_->WroteSilence(requested_frames, delay_frames); audio_clock_->WroteAudio(
0, requested_frames, delay_frames, playback_rate_);
return 0; return 0;
} }
// Mute audio by returning 0 when not playing. // Mute audio by returning 0 when not playing.
if (state_ != kPlaying) { if (state_ != kPlaying) {
audio_clock_->WroteSilence(requested_frames, delay_frames); audio_clock_->WroteAudio(
0, requested_frames, delay_frames, playback_rate_);
return 0; return 0;
} }
...@@ -576,20 +583,16 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, ...@@ -576,20 +583,16 @@ int AudioRendererImpl::Render(AudioBus* audio_bus,
// 3) We are in the kPlaying state // 3) We are in the kPlaying state
// //
// Otherwise the buffer has data we can send to the device. // Otherwise the buffer has data we can send to the device.
const base::TimeDelta media_timestamp_before_filling =
audio_clock_->CurrentMediaTimestamp(base::TimeDelta());
if (algorithm_->frames_buffered() > 0) { if (algorithm_->frames_buffered() > 0) {
frames_written = frames_written =
algorithm_->FillBuffer(audio_bus, requested_frames, playback_rate_); algorithm_->FillBuffer(audio_bus, requested_frames, playback_rate_);
audio_clock_->WroteAudio(
frames_written, delay_frames, playback_rate_, algorithm_->GetTime());
} }
audio_clock_->WroteSilence(requested_frames - frames_written, delay_frames); audio_clock_->WroteAudio(
frames_written, requested_frames, delay_frames, playback_rate_);
if (frames_written == 0) { if (frames_written == 0) {
if (received_end_of_stream_ && !rendered_end_of_stream_ && if (received_end_of_stream_ && !rendered_end_of_stream_ &&
audio_clock_->CurrentMediaTimestamp(base::TimeDelta()) == !audio_clock_->audio_data_buffered()) {
audio_clock_->last_endpoint_timestamp()) {
rendered_end_of_stream_ = true; rendered_end_of_stream_ = true;
task_runner_->PostTask(FROM_HERE, ended_cb_); task_runner_->PostTask(FROM_HERE, ended_cb_);
} else if (!received_end_of_stream_ && state_ == kPlaying) { } else if (!received_end_of_stream_ && state_ == kPlaying) {
...@@ -606,15 +609,18 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, ...@@ -606,15 +609,18 @@ int AudioRendererImpl::Render(AudioBus* audio_bus,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
// We only want to execute |time_cb_| if time has progressed and we haven't // Firing |ended_cb_| means we no longer need to run |time_cb_|.
// signaled end of stream yet. if (!rendered_end_of_stream_ &&
if (media_timestamp_before_filling != last_timestamp_update_ != audio_clock_->current_media_timestamp()) {
audio_clock_->CurrentMediaTimestamp(base::TimeDelta()) && // Since |max_time| uses linear interpolation, only provide an upper bound
!rendered_end_of_stream_) { // that is for audio data at the same playback rate. Failing to do so can
time_cb = // make time jump backwards when the linear interpolated time advances
base::Bind(time_cb_, // past buffered regions of audio at different rates.
audio_clock_->CurrentMediaTimestamp(base::TimeDelta()), last_timestamp_update_ = audio_clock_->current_media_timestamp();
audio_clock_->last_endpoint_timestamp()); base::TimeDelta max_time =
last_timestamp_update_ +
audio_clock_->contiguous_audio_data_buffered_at_same_rate();
time_cb = base::Bind(time_cb_, last_timestamp_update_, max_time);
} }
} }
......
...@@ -249,6 +249,7 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -249,6 +249,7 @@ class MEDIA_EXPORT AudioRendererImpl
scoped_ptr<AudioClock> audio_clock_; scoped_ptr<AudioClock> audio_clock_;
base::TimeDelta start_timestamp_; base::TimeDelta start_timestamp_;
base::TimeDelta last_timestamp_update_;
// End variables which must be accessed under |lock_|. ---------------------- // End variables which must be accessed under |lock_|. ----------------------
......
...@@ -62,6 +62,7 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -62,6 +62,7 @@ class AudioRendererImplTest : public ::testing::Test {
demuxer_stream_(DemuxerStream::AUDIO), demuxer_stream_(DemuxerStream::AUDIO),
decoder_(new MockAudioDecoder()), decoder_(new MockAudioDecoder()),
last_time_update_(kNoTimestamp()), last_time_update_(kNoTimestamp()),
last_max_time_(kNoTimestamp()),
ended_(false) { ended_(false) {
AudioDecoderConfig audio_config(kCodec, AudioDecoderConfig audio_config(kCodec,
kSampleFormat, kSampleFormat,
...@@ -117,6 +118,7 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -117,6 +118,7 @@ class AudioRendererImplTest : public ::testing::Test {
void OnAudioTimeCallback(TimeDelta current_time, TimeDelta max_time) { void OnAudioTimeCallback(TimeDelta current_time, TimeDelta max_time) {
CHECK(current_time <= max_time); CHECK(current_time <= max_time);
last_time_update_ = current_time; last_time_update_ = current_time;
last_max_time_ = max_time;
} }
void InitializeRenderer(const PipelineStatusCB& pipeline_status_cb) { void InitializeRenderer(const PipelineStatusCB& pipeline_status_cb) {
...@@ -334,6 +336,8 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -334,6 +336,8 @@ class AudioRendererImplTest : public ::testing::Test {
return last_time_update_; return last_time_update_;
} }
base::TimeDelta last_max_time() const { return last_max_time_; }
bool ended() const { return ended_; } bool ended() const { return ended_; }
// Fixture members. // Fixture members.
...@@ -404,6 +408,7 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -404,6 +408,7 @@ class AudioRendererImplTest : public ::testing::Test {
PipelineStatusCB init_decoder_cb_; PipelineStatusCB init_decoder_cb_;
base::TimeDelta last_time_update_; base::TimeDelta last_time_update_;
base::TimeDelta last_max_time_;
bool ended_; bool ended_;
DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest);
...@@ -628,6 +633,7 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) { ...@@ -628,6 +633,7 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) {
AudioTimestampHelper timestamp_helper(kOutputSamplesPerSecond); AudioTimestampHelper timestamp_helper(kOutputSamplesPerSecond);
EXPECT_EQ(kNoTimestamp(), last_time_update()); EXPECT_EQ(kNoTimestamp(), last_time_update());
EXPECT_EQ(kNoTimestamp(), last_max_time());
// Preroll() should be buffered some data, consume half of it now. // Preroll() should be buffered some data, consume half of it now.
OutputFrames frames_to_consume(frames_buffered().value / 2); OutputFrames frames_to_consume(frames_buffered().value / 2);
...@@ -639,17 +645,21 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) { ...@@ -639,17 +645,21 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) {
// a time update that's equal to |kFramesToConsume| from above. // a time update that's equal to |kFramesToConsume| from above.
timestamp_helper.SetBaseTimestamp(base::TimeDelta()); timestamp_helper.SetBaseTimestamp(base::TimeDelta());
timestamp_helper.AddFrames(frames_to_consume.value); timestamp_helper.AddFrames(frames_to_consume.value);
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update()); EXPECT_EQ(base::TimeDelta(), last_time_update());
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time());
// The next time update should match the remaining frames_buffered(), but only // The next time update should match the remaining frames_buffered(), but only
// after running the message loop. // after running the message loop.
frames_to_consume = frames_buffered(); frames_to_consume = frames_buffered();
EXPECT_TRUE(ConsumeBufferedData(frames_to_consume)); EXPECT_TRUE(ConsumeBufferedData(frames_to_consume));
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update()); EXPECT_EQ(base::TimeDelta(), last_time_update());
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time());
// Now the times should be updated.
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
timestamp_helper.AddFrames(frames_to_consume.value);
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update()); EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update());
timestamp_helper.AddFrames(frames_to_consume.value);
EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time());
} }
TEST_F(AudioRendererImplTest, ImmediateEndOfStream) { TEST_F(AudioRendererImplTest, ImmediateEndOfStream) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment