Commit 1d717e83 authored by dalecurtis@google.com's avatar dalecurtis@google.com

Add support for complete buffer discards.

Per discussion, allows kInfiniteDuration() for the front DiscardPadding
value.  When there's no decoder delay, this ensures accuracy when
discarding.  If decoder delay is present, the code falls back to using
the duration as an estimation.

BUG=371633
TEST=new unittests
NOTRY=true
R=wolenetz@chromium.org

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271928 0039d316-1c4b-4281-b951-d872f2087c98
parent b8ddfce1
......@@ -86,8 +86,13 @@ bool AudioDiscardHelper::ProcessBuffers(
// buffer's discard padding for processing with the current decoded buffer.
DecoderBuffer::DiscardPadding current_discard_padding =
encoded_buffer->discard_padding();
if (delayed_discard_)
if (delayed_discard_) {
// For simplicity disallow cases where decoder delay is present with delayed
// discard (no codecs at present). Doing so allows us to avoid complexity
// around endpoint tracking when handling complete buffer discards.
DCHECK_EQ(decoder_delay_, 0u);
std::swap(current_discard_padding, delayed_discard_padding_);
}
if (discard_frames_ > 0) {
const size_t decoded_frames = decoded_buffer->frame_count();
......@@ -110,8 +115,17 @@ bool AudioDiscardHelper::ProcessBuffers(
// Handle front discard padding.
if (current_discard_padding.first > base::TimeDelta()) {
const size_t decoded_frames = decoded_buffer->frame_count();
// If a complete buffer discard is requested and there's no decoder delay,
// just discard all remaining frames from this buffer. With decoder delay
// we have to estimate the correct number of frames to discard based on the
// duration of the encoded buffer.
const size_t start_frames_to_discard =
TimeDeltaToFrames(current_discard_padding.first);
current_discard_padding.first == kInfiniteDuration()
? (decoder_delay_ > 0
? TimeDeltaToFrames(encoded_buffer->duration())
: decoded_frames)
: TimeDeltaToFrames(current_discard_padding.first);
// Regardless of the timestamp on the encoded buffer, the corresponding
// decoded output will appear |decoder_delay_| frames later.
......
......@@ -306,12 +306,12 @@ TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPadding) {
decoded_buffer->frame_count());
}
TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPaddingAndCodecDelay) {
// Use a codec delay of 5ms.
const int kCodecDelay = kSampleRate / 100 / 2;
AudioDiscardHelper discard_helper(kSampleRate, kCodecDelay);
TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPaddingAndDecoderDelay) {
// Use a decoder delay of 5ms.
const int kDecoderDelay = kSampleRate / 100 / 2;
AudioDiscardHelper discard_helper(kSampleRate, kDecoderDelay);
ASSERT_FALSE(discard_helper.initialized());
discard_helper.Reset(kCodecDelay);
discard_helper.Reset(kDecoderDelay);
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
......@@ -330,12 +330,12 @@ TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPaddingAndCodecDelay) {
ASSERT_TRUE(discard_helper.initialized());
// Processing another buffer (with the same discard padding) should discard
// the back half of the buffer since kCodecDelay is half a buffer.
// the back half of the buffer since kDecoderDelay is half a buffer.
encoded_buffer->set_timestamp(kTimestamp + kDuration);
decoded_buffer = CreateDecodedBuffer(kTestFrames);
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
ASSERT_NEAR(kCodecDelay * kDataStep,
ExtractDecodedData(decoded_buffer, kCodecDelay),
ASSERT_NEAR(kDecoderDelay * kDataStep,
ExtractDecodedData(decoded_buffer, kDecoderDelay),
kDataStep * 1000);
ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
......@@ -381,4 +381,101 @@ TEST(AudioDiscardHelperTest, DelayedDiscardInitialDiscardAndDiscardPadding) {
decoded_buffer->frame_count());
}
TEST(AudioDiscardHelperTest, CompleteDiscard) {
AudioDiscardHelper discard_helper(kSampleRate, 0);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
discard_helper.Reset(0);
scoped_refptr<DecoderBuffer> encoded_buffer =
CreateEncodedBuffer(kTimestamp, kDuration);
encoded_buffer->set_discard_padding(
std::make_pair(kInfiniteDuration(), base::TimeDelta()));
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
// Verify all of the first buffer is discarded.
ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
ASSERT_TRUE(discard_helper.initialized());
encoded_buffer->set_timestamp(kTimestamp + kDuration);
encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
// Verify a second buffer goes through untouched.
decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, CompleteDiscardWithDelayedDiscard) {
AudioDiscardHelper discard_helper(kSampleRate, 0);
ASSERT_FALSE(discard_helper.initialized());
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
discard_helper.Reset(0);
scoped_refptr<DecoderBuffer> encoded_buffer =
CreateEncodedBuffer(kTimestamp, kDuration);
encoded_buffer->set_discard_padding(
std::make_pair(kInfiniteDuration(), base::TimeDelta()));
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
// Setup a delayed discard.
ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, NULL));
ASSERT_TRUE(discard_helper.initialized());
// Verify the first output buffer is dropped.
encoded_buffer->set_timestamp(kTimestamp + kDuration);
encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
// Verify the second buffer goes through untouched.
encoded_buffer->set_timestamp(kTimestamp + 2 * kDuration);
decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
}
TEST(AudioDiscardHelperTest, CompleteDiscardWithInitialDiscardDecoderDelay) {
// Use a decoder delay of 5ms.
const int kDecoderDelay = kSampleRate / 100 / 2;
AudioDiscardHelper discard_helper(kSampleRate, kDecoderDelay);
ASSERT_FALSE(discard_helper.initialized());
discard_helper.Reset(kDecoderDelay);
const base::TimeDelta kTimestamp = base::TimeDelta();
const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
scoped_refptr<DecoderBuffer> encoded_buffer =
CreateEncodedBuffer(kTimestamp, kDuration);
encoded_buffer->set_discard_padding(
std::make_pair(kInfiniteDuration(), base::TimeDelta()));
scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);
// Verify all of the first buffer is discarded.
ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
ASSERT_TRUE(discard_helper.initialized());
encoded_buffer->set_timestamp(kTimestamp + kDuration);
encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
// Verify 5ms off the front of the second buffer is discarded.
decoded_buffer = CreateDecodedBuffer(kTestFrames * 2);
ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
EXPECT_EQ(kDuration * 2 - kDuration / 2, decoded_buffer->duration());
EXPECT_EQ(kTestFrames * 2 - kDecoderDelay, decoded_buffer->frame_count());
ASSERT_FLOAT_EQ(kDecoderDelay * kDataStep,
ExtractDecodedData(decoded_buffer, 0));
}
} // namespace media
......@@ -108,7 +108,9 @@ class MEDIA_EXPORT DecoderBuffer
// A discard window indicates the amount of data which should be discard from
// this buffer after decoding. The first value is the amount of the front and
// the second the amount off the back.
// the second the amount off the back. A value of kInfiniteDuration() for the
// first value indicates the entire buffer should be discarded; the second
// value must be base::TimeDelta() in this case.
typedef std::pair<base::TimeDelta, base::TimeDelta> DiscardPadding;
const DiscardPadding& discard_padding() const {
DCHECK(!end_of_stream());
......
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