OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/memory/scoped_ptr.h" |
| 6 #include "media/base/audio_buffer.h" |
| 7 #include "media/base/audio_bus.h" |
| 8 #include "media/base/audio_discard_helper.h" |
| 9 #include "media/base/buffers.h" |
| 10 #include "media/base/decoder_buffer.h" |
| 11 #include "media/base/test_helpers.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 |
| 14 namespace media { |
| 15 |
| 16 static const float kDataStep = 0.01f; |
| 17 static const size_t kSampleRate = 48000; |
| 18 |
| 19 static scoped_refptr<DecoderBuffer> CreateEncodedBuffer( |
| 20 base::TimeDelta timestamp, |
| 21 base::TimeDelta duration) { |
| 22 scoped_refptr<DecoderBuffer> result(new DecoderBuffer(1)); |
| 23 result->set_timestamp(timestamp); |
| 24 result->set_duration(duration); |
| 25 return result; |
| 26 } |
| 27 |
| 28 static scoped_refptr<AudioBuffer> CreateDecodedBuffer(int frames) { |
| 29 return MakeAudioBuffer(kSampleFormatPlanarF32, |
| 30 CHANNEL_LAYOUT_MONO, |
| 31 1, |
| 32 kSampleRate, |
| 33 0.0f, |
| 34 kDataStep, |
| 35 frames, |
| 36 kNoTimestamp(), |
| 37 kNoTimestamp()); |
| 38 } |
| 39 |
| 40 static float ExtractDecodedData(const scoped_refptr<AudioBuffer>& buffer, |
| 41 int index) { |
| 42 // This is really inefficient, but we can't access the raw AudioBuffer if any |
| 43 // start trimming has been applied. |
| 44 scoped_ptr<AudioBus> temp_bus = AudioBus::Create(buffer->channel_count(), 1); |
| 45 buffer->ReadFrames(1, index, 0, temp_bus.get()); |
| 46 return temp_bus->channel(0)[0]; |
| 47 } |
| 48 |
| 49 TEST(AudioDiscardHelperTest, TimeDeltaToFrames) { |
| 50 AudioDiscardHelper discard_helper(kSampleRate); |
| 51 |
| 52 EXPECT_EQ(0u, discard_helper.TimeDeltaToFrames(base::TimeDelta())); |
| 53 EXPECT_EQ( |
| 54 kSampleRate / 100, |
| 55 discard_helper.TimeDeltaToFrames(base::TimeDelta::FromMilliseconds(10))); |
| 56 |
| 57 // Ensure partial frames are rounded down correctly. The equation below |
| 58 // calculates a frame count with a fractional part < 0.5. |
| 59 const int small_remainder = |
| 60 base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.9) / kSampleRate; |
| 61 EXPECT_EQ(kSampleRate - 1, |
| 62 discard_helper.TimeDeltaToFrames( |
| 63 base::TimeDelta::FromMicroseconds(small_remainder))); |
| 64 |
| 65 // Ditto, but rounded up using a fractional part > 0.5. |
| 66 const int large_remainder = |
| 67 base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.4) / kSampleRate; |
| 68 EXPECT_EQ(kSampleRate, |
| 69 discard_helper.TimeDeltaToFrames( |
| 70 base::TimeDelta::FromMicroseconds(large_remainder))); |
| 71 } |
| 72 |
| 73 TEST(AudioDiscardHelperTest, BasicProcessBuffers) { |
| 74 AudioDiscardHelper discard_helper(kSampleRate); |
| 75 ASSERT_FALSE(discard_helper.initialized()); |
| 76 |
| 77 const base::TimeDelta kTimestamp = base::TimeDelta(); |
| 78 |
| 79 // Use an estimated duration which doesn't match the number of decoded frames |
| 80 // to ensure the helper is correctly setting durations based on output frames. |
| 81 const base::TimeDelta kEstimatedDuration = |
| 82 base::TimeDelta::FromMilliseconds(9); |
| 83 const base::TimeDelta kActualDuration = base::TimeDelta::FromMilliseconds(10); |
| 84 const int kTestFrames = discard_helper.TimeDeltaToFrames(kActualDuration); |
| 85 |
| 86 scoped_refptr<DecoderBuffer> encoded_buffer = |
| 87 CreateEncodedBuffer(kTimestamp, kEstimatedDuration); |
| 88 scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 89 |
| 90 // Verify the basic case where nothing is discarded. |
| 91 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 92 ASSERT_TRUE(discard_helper.initialized()); |
| 93 EXPECT_EQ(kTimestamp, decoded_buffer->timestamp()); |
| 94 EXPECT_EQ(kActualDuration, decoded_buffer->duration()); |
| 95 EXPECT_EQ(kTestFrames, decoded_buffer->frame_count()); |
| 96 |
| 97 // Verify a Reset() takes us back to an uninitialized state. |
| 98 discard_helper.Reset(0); |
| 99 ASSERT_FALSE(discard_helper.initialized()); |
| 100 |
| 101 // Verify a NULL output buffer returns false. |
| 102 ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, NULL)); |
| 103 } |
| 104 |
| 105 TEST(AudioDiscardHelperTest, NegativeTimestampClampsToZero) { |
| 106 AudioDiscardHelper discard_helper(kSampleRate); |
| 107 ASSERT_FALSE(discard_helper.initialized()); |
| 108 |
| 109 const base::TimeDelta kTimestamp = -base::TimeDelta::FromSeconds(1); |
| 110 const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10); |
| 111 const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration); |
| 112 |
| 113 scoped_refptr<DecoderBuffer> encoded_buffer = |
| 114 CreateEncodedBuffer(kTimestamp, kDuration); |
| 115 scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 116 |
| 117 // Verify the basic case where nothing is discarded. |
| 118 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 119 ASSERT_TRUE(discard_helper.initialized()); |
| 120 EXPECT_EQ(base::TimeDelta(), decoded_buffer->timestamp()); |
| 121 EXPECT_EQ(kDuration, decoded_buffer->duration()); |
| 122 EXPECT_EQ(kTestFrames, decoded_buffer->frame_count()); |
| 123 } |
| 124 |
| 125 TEST(AudioDiscardHelperTest, ProcessBuffersWithInitialDiscard) { |
| 126 AudioDiscardHelper discard_helper(kSampleRate); |
| 127 ASSERT_FALSE(discard_helper.initialized()); |
| 128 |
| 129 const base::TimeDelta kTimestamp = base::TimeDelta(); |
| 130 const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10); |
| 131 const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration); |
| 132 |
| 133 // Tell the helper we want to discard half of the initial frames. |
| 134 const int kDiscardFrames = kTestFrames / 2; |
| 135 discard_helper.Reset(kDiscardFrames); |
| 136 |
| 137 scoped_refptr<DecoderBuffer> encoded_buffer = |
| 138 CreateEncodedBuffer(kTimestamp, kDuration); |
| 139 scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 140 |
| 141 // Verify half the frames end up discarded. |
| 142 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 143 ASSERT_TRUE(discard_helper.initialized()); |
| 144 EXPECT_EQ(kTimestamp, decoded_buffer->timestamp()); |
| 145 EXPECT_EQ(kDuration / 2, decoded_buffer->duration()); |
| 146 EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count()); |
| 147 ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep, |
| 148 ExtractDecodedData(decoded_buffer, 0)); |
| 149 } |
| 150 |
| 151 TEST(AudioDiscardHelperTest, ProcessBuffersWithLargeInitialDiscard) { |
| 152 AudioDiscardHelper discard_helper(kSampleRate); |
| 153 ASSERT_FALSE(discard_helper.initialized()); |
| 154 |
| 155 const base::TimeDelta kTimestamp = base::TimeDelta(); |
| 156 const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10); |
| 157 const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration); |
| 158 |
| 159 // Tell the helper we want to discard 1.5 buffers worth of frames. |
| 160 discard_helper.Reset(kTestFrames * 1.5); |
| 161 |
| 162 scoped_refptr<DecoderBuffer> encoded_buffer = |
| 163 CreateEncodedBuffer(kTimestamp, kDuration); |
| 164 scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 165 |
| 166 // The first call should fail since no output buffer remains. |
| 167 ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 168 ASSERT_TRUE(discard_helper.initialized()); |
| 169 |
| 170 // Generate another set of buffers and expect half the output frames. |
| 171 encoded_buffer = CreateEncodedBuffer(kTimestamp + kDuration, kDuration); |
| 172 decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 173 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 174 |
| 175 // The timestamp should match that of the initial buffer. |
| 176 const int kDiscardFrames = kTestFrames / 2; |
| 177 EXPECT_EQ(kTimestamp, decoded_buffer->timestamp()); |
| 178 EXPECT_EQ(kDuration / 2, decoded_buffer->duration()); |
| 179 EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count()); |
| 180 ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep, |
| 181 ExtractDecodedData(decoded_buffer, 0)); |
| 182 } |
| 183 |
| 184 TEST(AudioDiscardHelperTest, AllowNonMonotonicTimestamps) { |
| 185 AudioDiscardHelper discard_helper(kSampleRate); |
| 186 ASSERT_FALSE(discard_helper.initialized()); |
| 187 |
| 188 const base::TimeDelta kTimestamp = base::TimeDelta(); |
| 189 const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10); |
| 190 const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration); |
| 191 |
| 192 scoped_refptr<DecoderBuffer> encoded_buffer = |
| 193 CreateEncodedBuffer(kTimestamp, kDuration); |
| 194 scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 195 |
| 196 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 197 ASSERT_TRUE(discard_helper.initialized()); |
| 198 EXPECT_EQ(kTimestamp, decoded_buffer->timestamp()); |
| 199 EXPECT_EQ(kDuration, decoded_buffer->duration()); |
| 200 EXPECT_EQ(kTestFrames, decoded_buffer->frame_count()); |
| 201 |
| 202 // Process the same input buffer again to ensure input timestamps which go |
| 203 // backwards in time are not errors. |
| 204 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 205 EXPECT_EQ(kTimestamp + kDuration, decoded_buffer->timestamp()); |
| 206 EXPECT_EQ(kDuration, decoded_buffer->duration()); |
| 207 EXPECT_EQ(kTestFrames, decoded_buffer->frame_count()); |
| 208 } |
| 209 |
| 210 TEST(AudioDiscardHelperTest, DiscardPadding) { |
| 211 AudioDiscardHelper discard_helper(kSampleRate); |
| 212 ASSERT_FALSE(discard_helper.initialized()); |
| 213 |
| 214 const base::TimeDelta kTimestamp = base::TimeDelta(); |
| 215 const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10); |
| 216 const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration); |
| 217 |
| 218 scoped_refptr<DecoderBuffer> encoded_buffer = |
| 219 CreateEncodedBuffer(kTimestamp, kDuration); |
| 220 scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 221 |
| 222 // Set a discard padding equivalent to half the buffer. |
| 223 encoded_buffer->set_discard_padding(kDuration / 2); |
| 224 |
| 225 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 226 ASSERT_TRUE(discard_helper.initialized()); |
| 227 EXPECT_EQ(kTimestamp, decoded_buffer->timestamp()); |
| 228 EXPECT_EQ(kDuration / 2, decoded_buffer->duration()); |
| 229 EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count()); |
| 230 ASSERT_FLOAT_EQ(0, ExtractDecodedData(decoded_buffer, 0)); |
| 231 } |
| 232 |
| 233 TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPadding) { |
| 234 AudioDiscardHelper discard_helper(kSampleRate); |
| 235 ASSERT_FALSE(discard_helper.initialized()); |
| 236 |
| 237 const base::TimeDelta kTimestamp = base::TimeDelta(); |
| 238 const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10); |
| 239 const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration); |
| 240 |
| 241 scoped_refptr<DecoderBuffer> encoded_buffer = |
| 242 CreateEncodedBuffer(kTimestamp, kDuration); |
| 243 scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames); |
| 244 |
| 245 // Set a discard padding equivalent to a quarter of the buffer. |
| 246 encoded_buffer->set_discard_padding(kDuration / 4); |
| 247 |
| 248 // Set an initial discard of a quarter of the buffer. |
| 249 const int kDiscardFrames = kTestFrames / 4; |
| 250 discard_helper.Reset(kDiscardFrames); |
| 251 |
| 252 ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer)); |
| 253 ASSERT_TRUE(discard_helper.initialized()); |
| 254 EXPECT_EQ(kTimestamp, decoded_buffer->timestamp()); |
| 255 EXPECT_EQ(kDuration / 2, decoded_buffer->duration()); |
| 256 EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count()); |
| 257 ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep, |
| 258 ExtractDecodedData(decoded_buffer, 0)); |
| 259 } |
| 260 |
| 261 } // namespace media |
OLD | NEW |