Index: media/base/audio_discard_helper_unittest.cc |
diff --git a/media/base/audio_discard_helper_unittest.cc b/media/base/audio_discard_helper_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7788e7f6c49e4824726430c2cd71c709cf1c626b |
--- /dev/null |
+++ b/media/base/audio_discard_helper_unittest.cc |
@@ -0,0 +1,261 @@ |
+// 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(), |
+ 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); |
+ |
+ 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); |
+ 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); |
+ 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); |
+ 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); |
+ 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); |
+ 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, DiscardPadding) { |
+ AudioDiscardHelper discard_helper(kSampleRate); |
+ 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(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()); |
+ ASSERT_FLOAT_EQ(0, ExtractDecodedData(decoded_buffer, 0)); |
+} |
+ |
+TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPadding) { |
+ AudioDiscardHelper discard_helper(kSampleRate); |
+ 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(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)); |
+} |
+ |
+} // namespace media |