普通文本  |  552行  |  24.07 KB

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/memory/scoped_ptr.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_discard_helper.h"
#include "media/base/buffers.h"
#include "media/base/decoder_buffer.h"
#include "media/base/test_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media {

static const float kDataStep = 0.01f;
static const size_t kSampleRate = 48000;

static scoped_refptr<DecoderBuffer> CreateEncodedBuffer(
    base::TimeDelta timestamp,
    base::TimeDelta duration) {
  scoped_refptr<DecoderBuffer> result(new DecoderBuffer(1));
  result->set_timestamp(timestamp);
  result->set_duration(duration);
  return result;
}

static scoped_refptr<AudioBuffer> CreateDecodedBuffer(int frames) {
  return MakeAudioBuffer(kSampleFormatPlanarF32,
                         CHANNEL_LAYOUT_MONO,
                         1,
                         kSampleRate,
                         0.0f,
                         kDataStep,
                         frames,
                         kNoTimestamp());
}

static float ExtractDecodedData(const scoped_refptr<AudioBuffer>& buffer,
                                int index) {
  // This is really inefficient, but we can't access the raw AudioBuffer if any
  // start trimming has been applied.
  scoped_ptr<AudioBus> temp_bus = AudioBus::Create(buffer->channel_count(), 1);
  buffer->ReadFrames(1, index, 0, temp_bus.get());
  return temp_bus->channel(0)[0];
}

TEST(AudioDiscardHelperTest, TimeDeltaToFrames) {
  AudioDiscardHelper discard_helper(kSampleRate, 0);

  EXPECT_EQ(0u, discard_helper.TimeDeltaToFrames(base::TimeDelta()));
  EXPECT_EQ(
      kSampleRate / 100,
      discard_helper.TimeDeltaToFrames(base::TimeDelta::FromMilliseconds(10)));

  // Ensure partial frames are rounded down correctly.  The equation below
  // calculates a frame count with a fractional part < 0.5.
  const int small_remainder =
      base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.9) / kSampleRate;
  EXPECT_EQ(kSampleRate - 1,
            discard_helper.TimeDeltaToFrames(
                base::TimeDelta::FromMicroseconds(small_remainder)));

  // Ditto, but rounded up using a fractional part > 0.5.
  const int large_remainder =
      base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.4) / kSampleRate;
  EXPECT_EQ(kSampleRate,
            discard_helper.TimeDeltaToFrames(
                base::TimeDelta::FromMicroseconds(large_remainder)));
}

TEST(AudioDiscardHelperTest, BasicProcessBuffers) {
  AudioDiscardHelper discard_helper(kSampleRate, 0);
  ASSERT_FALSE(discard_helper.initialized());

  const base::TimeDelta kTimestamp = base::TimeDelta();

  // Use an estimated duration which doesn't match the number of decoded frames
  // to ensure the helper is correctly setting durations based on output frames.
  const base::TimeDelta kEstimatedDuration =
      base::TimeDelta::FromMilliseconds(9);
  const base::TimeDelta kActualDuration = base::TimeDelta::FromMilliseconds(10);
  const int kTestFrames = discard_helper.TimeDeltaToFrames(kActualDuration);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kEstimatedDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Verify the basic case where nothing is discarded.
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kActualDuration, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());

  // Verify a Reset() takes us back to an uninitialized state.
  discard_helper.Reset(0);
  ASSERT_FALSE(discard_helper.initialized());

  // Verify a NULL output buffer returns false.
  ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, NULL));
}

TEST(AudioDiscardHelperTest, NegativeTimestampClampsToZero) {
  AudioDiscardHelper discard_helper(kSampleRate, 0);
  ASSERT_FALSE(discard_helper.initialized());

  const base::TimeDelta kTimestamp = -base::TimeDelta::FromSeconds(1);
  const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
  const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Verify the basic case where nothing is discarded.
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
  EXPECT_EQ(base::TimeDelta(), decoded_buffer->timestamp());
  EXPECT_EQ(kDuration, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, ProcessBuffersWithInitialDiscard) {
  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);

  // Tell the helper we want to discard half of the initial frames.
  const int kDiscardFrames = kTestFrames / 2;
  discard_helper.Reset(kDiscardFrames);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Verify half the frames end up discarded.
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
  EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
  ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
                  ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, ProcessBuffersWithLargeInitialDiscard) {
  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);

  // Tell the helper we want to discard 1.5 buffers worth of frames.
  discard_helper.Reset(kTestFrames * 1.5);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // The first call should fail since no output buffer remains.
  ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());

  // Generate another set of buffers and expect half the output frames.
  encoded_buffer = CreateEncodedBuffer(kTimestamp + kDuration, kDuration);
  decoded_buffer = CreateDecodedBuffer(kTestFrames);
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));

  // The timestamp should match that of the initial buffer.
  const int kDiscardFrames = kTestFrames / 2;
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
  EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
  ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
                  ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, AllowNonMonotonicTimestamps) {
  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);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());

  // Process the same input buffer again to ensure input timestamps which go
  // backwards in time are not errors.
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  EXPECT_EQ(kTimestamp + kDuration, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, DiscardEndPadding) {
  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);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Set a discard padding equivalent to half the buffer.
  encoded_buffer->set_discard_padding(
      std::make_pair(base::TimeDelta(), kDuration / 2));

  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, BadDiscardEndPadding) {
  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);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Set a discard padding equivalent to double the buffer size.
  encoded_buffer->set_discard_padding(
      std::make_pair(base::TimeDelta(), kDuration * 2));

  // Verify the end discard padding is rejected.
  ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
}

TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardEndPadding) {
  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);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Set a discard padding equivalent to a quarter of the buffer.
  encoded_buffer->set_discard_padding(
      std::make_pair(base::TimeDelta(), kDuration / 4));

  // Set an initial discard of a quarter of the buffer.
  const int kDiscardFrames = kTestFrames / 4;
  discard_helper.Reset(kDiscardFrames);

  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
  ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
                  ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPadding) {
  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);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Set all the discard values to be different to ensure each is properly used.
  const int kDiscardFrames = kTestFrames / 4;
  encoded_buffer->set_discard_padding(
      std::make_pair(kDuration / 8, kDuration / 16));
  discard_helper.Reset(kDiscardFrames);

  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
            decoded_buffer->duration());
  EXPECT_EQ(kTestFrames - kTestFrames / 4 - kTestFrames / 8 - kTestFrames / 16,
            decoded_buffer->frame_count());
}

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(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);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Set a discard padding equivalent to half of the buffer.
  encoded_buffer->set_discard_padding(
      std::make_pair(kDuration / 2, base::TimeDelta()));

  // All of the first buffer should be discarded, half from the inital delay and
  // another half from the front discard padding.
  //
  //    Encoded                   Discard Delay
  //   |--------|     |---------|     |----|
  //   |AAAAAAAA| --> |....|AAAA| --> |AAAA| -------> NULL
  //   |--------|     |---------|     |----|
  //                    Decoded               Discard Front Padding
  //
  ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  ASSERT_TRUE(discard_helper.initialized());

  // Processing another buffer that has front discard set to half the buffer's
  // duration should discard the back half of the buffer since kDecoderDelay is
  // half a buffer.  The end padding should not be discarded until another
  // buffer is processed.  kDuration / 4 is chosen for the end discard since it
  // will force the end discard to start after position zero within the next
  // decoded buffer.
  //
  //    Encoded                    Discard Front Padding (from B)
  //   |--------|     |---------|             |----|
  //   |BBBBBBBB| --> |AAAA|BBBB| ----------> |AAAA|
  //   |--------|     |---------|             |----|
  //                    Decoded
  //           (includes carryover from A)
  //
  encoded_buffer->set_timestamp(encoded_buffer->timestamp() + kDuration);
  encoded_buffer->set_discard_padding(
      std::make_pair(kDuration / 2, kDuration / 4));
  decoded_buffer = CreateDecodedBuffer(kTestFrames);
  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
  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());
  EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());

  // Verify it was actually the latter half of the buffer that was removed.
  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));

  // Verify the end discard padding is carried over to the next buffer.  Use
  // kDuration / 2 for the end discard padding so that the next buffer has its
  // start entirely discarded.
  //
  //    Encoded                      Discard End Padding (from B)
  //   |--------|     |---------|             |-------|
  //   |CCCCCCCC| --> |BBBB|CCCC| ----------> |BB|CCCC|
  //   |--------|     |---------|             |-------|
  //                    Decoded
  //           (includes carryover from B)
  //
  encoded_buffer->set_timestamp(encoded_buffer->timestamp() + kDuration);
  encoded_buffer->set_discard_padding(
      std::make_pair(base::TimeDelta(), kDuration / 2));
  decoded_buffer = CreateDecodedBuffer(kTestFrames);
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  EXPECT_EQ(kTimestamp + kDuration / 2, decoded_buffer->timestamp());
  EXPECT_EQ(3 * kDuration / 4, decoded_buffer->duration());
  EXPECT_EQ(3 * kTestFrames / 4, decoded_buffer->frame_count());

  // Verify it was actually the second quarter of the buffer that was removed.
  const int kDiscardFrames = kTestFrames / 4;
  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
  ASSERT_FLOAT_EQ(
      kDiscardFrames * 2 * kDataStep,
      ExtractDecodedData(decoded_buffer, kDecoderDelay - kDiscardFrames));

  // One last test to ensure carryover discard from the start works.
  //
  //    Encoded                      Discard End Padding (from C)
  //   |--------|     |---------|             |----|
  //   |DDDDDDDD| --> |CCCC|DDDD| ----------> |DDDD|
  //   |--------|     |---------|             |----|
  //                    Decoded
  //           (includes carryover from C)
  //
  encoded_buffer->set_timestamp(encoded_buffer->timestamp() + kDuration);
  encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
  decoded_buffer = CreateDecodedBuffer(kTestFrames);
  ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  EXPECT_EQ(kTimestamp + kDuration / 2 + 3 * kDuration / 4,
            decoded_buffer->timestamp());
  EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
  EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
  ASSERT_FLOAT_EQ(kTestFrames / 2 * kDataStep,
                  ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, DelayedDiscardInitialDiscardAndDiscardPadding) {
  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);

  scoped_refptr<DecoderBuffer> encoded_buffer =
      CreateEncodedBuffer(kTimestamp, kDuration);

  // Set all the discard values to be different to ensure each is properly used.
  const int kDiscardFrames = kTestFrames / 4;
  encoded_buffer->set_discard_padding(
      std::make_pair(kDuration / 8, kDuration / 16));
  discard_helper.Reset(kDiscardFrames);

  // Verify nothing is output for the first buffer, yet initialized is true.
  ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, NULL));
  ASSERT_TRUE(discard_helper.initialized());

  // Create an encoded buffer with no discard padding.
  encoded_buffer = CreateEncodedBuffer(kTimestamp + kDuration, kDuration);
  scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

  // Verify that when the decoded buffer is consumed, the discards from the
  // previous encoded buffer are applied.
  ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
  EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
  EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
            decoded_buffer->duration());
  EXPECT_EQ(kTestFrames - kTestFrames / 4 - kTestFrames / 8 - kTestFrames / 16,
            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