text: // 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 "media/base/audio_discard_helper.h"
  
  #include <stddef.h>
  
  #include <memory>
  
  #include "media/base/audio_buffer.h"
  #include "media/base/audio_bus.h"
  #include "media/base/decoder_buffer.h"
  #include "media/base/test_helpers.h"
  #include "media/base/timestamp_constants.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 AudioBuffer& buffer, int index) {
    // This is really inefficient, but we can't access the raw AudioBuffer if any
    // start trimming has been applied.
    std::unique_ptr<AudioBus> temp_bus =
        AudioBus::Create(buffer.channel_count(), 1);
    buffer.ReadFrames(1, index, 0, temp_bus.get());
    return temp_bus->channel(0)[0] * std::numeric_limits<uint16_t>::max();
  }
  
  TEST(AudioDiscardHelperTest, TimeDeltaToFrames) {
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
  
    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, false);
    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.get()));
    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, false);
    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.get()));
    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, false);
    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.get()));
    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, false);
    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.get()));
    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.get()));
  
    // 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, false);
    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.get()));
    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.get()));
    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, false);
    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.get()));
    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, false);
    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.get()));
    ASSERT_TRUE(discard_helper.initialized());
  }
  
  TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardEndPadding) {
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    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.get()));
    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, false);
    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.get()));
    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, false);
    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.get()));
    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.get()));
    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.get()));
    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.get()));
    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, true);
    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.get()));
    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, false);
    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.get()));
    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.get()));
    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, true);
    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.get()));
  
    // 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.get()));
    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, false);
    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.get()));
    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.get()));
    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
  