| // 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 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. |
| 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]; |
| } |
| |
| 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)); |
| 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)); |
| 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)); |
| 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)); |
| 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, 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)); |
| 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, 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)); |
| 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)); |
| 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)); |
| 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)); |
| 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)); |
| 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, 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)); |
| 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)); |
| 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, 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)); |
| |
| // 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, 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)); |
| 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 |