OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // The format of these tests are to enqueue a known amount of data and then | 5 // The format of these tests are to enqueue a known amount of data and then |
6 // request the exact amount we expect in order to dequeue the known amount of | 6 // request the exact amount we expect in order to dequeue the known amount of |
7 // data. This ensures that for any rate we are consuming input data at the | 7 // data. This ensures that for any rate we are consuming input data at the |
8 // correct rate. We always pass in a very large destination buffer with the | 8 // correct rate. We always pass in a very large destination buffer with the |
9 // expectation that FillBuffer() will fill as much as it can but no more. | 9 // expectation that FillBuffer() will fill as much as it can but no more. |
10 | 10 |
| 11 #include <cmath> |
| 12 |
11 #include "base/bind.h" | 13 #include "base/bind.h" |
12 #include "base/callback.h" | 14 #include "base/callback.h" |
13 #include "media/base/data_buffer.h" | 15 #include "media/base/data_buffer.h" |
14 #include "media/filters/audio_renderer_algorithm_base.h" | 16 #include "media/filters/audio_renderer_algorithm_base.h" |
15 #include "testing/gmock/include/gmock/gmock.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | 17 #include "testing/gtest/include/gtest/gtest.h" |
17 | 18 |
18 using ::testing::AnyNumber; | 19 static const size_t kRawDataSize = 10 * 1024; |
| 20 static const int kSamplesPerSecond = 44100; |
| 21 static const int kDefaultChannels = 2; |
| 22 static const int kDefaultSampleBits = 16; |
19 | 23 |
20 namespace media { | 24 namespace media { |
21 | 25 |
22 static const int kChannels = 1; | 26 class AudioRendererAlgorithmBaseTest : public testing::Test { |
23 static const int kSampleRate = 1000; | 27 public: |
24 static const int kSampleBits = 8; | 28 AudioRendererAlgorithmBaseTest() |
25 | 29 : bytes_enqueued_(0) { |
26 TEST(AudioRendererAlgorithmBaseTest, FillBuffer_NormalRate) { | 30 } |
27 // When playback rate == 1.0f: straight copy of whatever is in |queue_|. | 31 |
28 AudioRendererAlgorithmBase algorithm; | 32 ~AudioRendererAlgorithmBaseTest() {} |
29 algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 1.0f, | 33 |
30 base::Bind(&base::DoNothing)); | 34 void Initialize() { |
31 | 35 Initialize(kDefaultChannels, kDefaultSampleBits); |
32 // Enqueue a buffer of any size since it doesn't matter. | 36 } |
33 const size_t kDataSize = 1024; | 37 |
34 algorithm.EnqueueBuffer(new DataBuffer( | 38 void Initialize(int channels, int bits_per_channel) { |
35 scoped_array<uint8>(new uint8[kDataSize]), kDataSize)); | 39 algorithm_.Initialize( |
36 EXPECT_EQ(kDataSize, algorithm.bytes_buffered()); | 40 channels, kSamplesPerSecond, bits_per_channel, 1.0f, |
37 | 41 base::Bind(&AudioRendererAlgorithmBaseTest::EnqueueData, |
38 // Read the same sized amount. | 42 base::Unretained(this))); |
39 scoped_array<uint8> data(new uint8[kDataSize]); | 43 EnqueueData(); |
40 EXPECT_EQ(kDataSize, algorithm.FillBuffer(data.get(), kDataSize)); | 44 } |
41 EXPECT_EQ(0u, algorithm.bytes_buffered()); | 45 |
42 } | 46 void EnqueueData() { |
43 | 47 scoped_array<uint8> audio_data(new uint8[kRawDataSize]); |
44 TEST(AudioRendererAlgorithmBaseTest, FillBuffer_DoubleRate) { | 48 CHECK_EQ(kRawDataSize % algorithm_.bytes_per_channel(), 0u); |
45 // When playback rate > 1.0f: input is read faster than output is written. | 49 CHECK_EQ(kRawDataSize % algorithm_.bytes_per_frame(), 0u); |
46 AudioRendererAlgorithmBase algorithm; | 50 size_t length = kRawDataSize / algorithm_.bytes_per_channel(); |
47 algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 2.0f, | 51 switch (algorithm_.bytes_per_channel()) { |
48 base::Bind(&base::DoNothing)); | 52 case 4: |
49 | 53 WriteFakeData<int32>(audio_data.get(), length); |
50 // First parameter is the input buffer size, second parameter is how much data | 54 break; |
51 // we expect to consume in order to have no data left in the |algorithm|. | 55 case 2: |
52 // | 56 WriteFakeData<int16>(audio_data.get(), length); |
53 // For rate == 0.5f, reading half the input size should consume all enqueued | 57 break; |
54 // data. | 58 case 1: |
55 const size_t kBufferSize = 16 * 1024; | 59 WriteFakeData<uint8>(audio_data.get(), length); |
56 scoped_array<uint8> data(new uint8[kBufferSize]); | 60 break; |
57 const size_t kTestData[][2] = { | 61 default: |
58 { algorithm.window_size(), algorithm.window_size() / 2}, | 62 NOTREACHED() << "Unsupported audio bit depth in crossfade."; |
59 { algorithm.window_size() / 2, algorithm.window_size() / 4}, | 63 } |
60 { 4u, 2u }, | 64 algorithm_.EnqueueBuffer(new DataBuffer(audio_data.Pass(), kRawDataSize)); |
61 { 0u, 0u }, | 65 bytes_enqueued_ += kRawDataSize; |
62 }; | 66 } |
63 | 67 |
64 for (size_t i = 0u; i < arraysize(kTestData); ++i) { | 68 template <class Type> |
65 const size_t kDataSize = kTestData[i][0]; | 69 void WriteFakeData(uint8* audio_data, size_t length) { |
66 algorithm.EnqueueBuffer(new DataBuffer( | 70 Type* output = reinterpret_cast<Type*>(audio_data); |
67 scoped_array<uint8>(new uint8[kDataSize]), kDataSize)); | 71 for (size_t i = 0; i < length; i++) { |
68 EXPECT_EQ(kDataSize, algorithm.bytes_buffered()); | 72 // The value of the data is meaningless; we just want non-zero data to |
69 | 73 // differentiate it from muted data. |
70 const size_t kExpectedSize = kTestData[i][1]; | 74 output[i] = i % 5 + 10; |
71 ASSERT_LE(kExpectedSize, kBufferSize); | 75 } |
72 EXPECT_EQ(kExpectedSize, algorithm.FillBuffer(data.get(), kBufferSize)); | 76 } |
73 EXPECT_EQ(0u, algorithm.bytes_buffered()); | 77 |
74 } | 78 void CheckFakeData(uint8* audio_data, int frames_written, |
75 } | 79 double playback_rate) { |
76 | 80 size_t length = |
77 TEST(AudioRendererAlgorithmBaseTest, FillBuffer_HalfRate) { | 81 (frames_written * algorithm_.bytes_per_frame()) |
78 // When playback rate < 1.0f: input is read slower than output is written. | 82 / algorithm_.bytes_per_channel(); |
79 AudioRendererAlgorithmBase algorithm; | 83 |
80 algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 0.5f, | 84 switch (algorithm_.bytes_per_channel()) { |
81 base::Bind(&base::DoNothing)); | 85 case 4: |
82 | 86 DoCheckFakeData<int32>(audio_data, length); |
83 // First parameter is the input buffer size, second parameter is how much data | 87 break; |
84 // we expect to consume in order to have no data left in the |algorithm|. | 88 case 2: |
85 // | 89 DoCheckFakeData<int16>(audio_data, length); |
86 // For rate == 0.5f, reading double the input size should consume all enqueued | 90 break; |
87 // data. | 91 case 1: |
88 const size_t kBufferSize = 16 * 1024; | 92 DoCheckFakeData<uint8>(audio_data, length); |
89 scoped_array<uint8> data(new uint8[kBufferSize]); | 93 break; |
90 const size_t kTestData[][2] = { | 94 default: |
91 { algorithm.window_size(), algorithm.window_size() * 2 }, | 95 NOTREACHED() << "Unsupported audio bit depth in crossfade."; |
92 { algorithm.window_size() / 2, algorithm.window_size() }, | 96 } |
93 { 2u, 4u }, | 97 } |
94 { 0u, 0u }, | 98 |
95 }; | 99 template <class Type> |
96 | 100 void DoCheckFakeData(uint8* audio_data, size_t length) { |
97 for (size_t i = 0u; i < arraysize(kTestData); ++i) { | 101 Type* output = reinterpret_cast<Type*>(audio_data); |
98 const size_t kDataSize = kTestData[i][0]; | 102 for (size_t i = 0; i < length; i++) { |
99 algorithm.EnqueueBuffer(new DataBuffer( | 103 EXPECT_TRUE(algorithm_.is_muted() || output[i] != 0); |
100 scoped_array<uint8>(new uint8[kDataSize]), kDataSize)); | 104 } |
101 EXPECT_EQ(kDataSize, algorithm.bytes_buffered()); | 105 } |
102 | 106 |
103 const size_t kExpectedSize = kTestData[i][1]; | 107 int ComputeConsumedBytes(int initial_bytes_enqueued, |
104 ASSERT_LE(kExpectedSize, kBufferSize); | 108 int initial_bytes_buffered) { |
105 EXPECT_EQ(kExpectedSize, algorithm.FillBuffer(data.get(), kBufferSize)); | 109 int byte_delta = bytes_enqueued_ - initial_bytes_enqueued; |
106 EXPECT_EQ(0u, algorithm.bytes_buffered()); | 110 int buffered_delta = algorithm_.bytes_buffered() - initial_bytes_buffered; |
107 } | 111 int consumed = byte_delta - buffered_delta; |
108 } | 112 CHECK_GE(consumed, 0); |
109 | 113 return consumed; |
110 TEST(AudioRendererAlgorithmBaseTest, FillBuffer_QuarterRate) { | 114 } |
111 // When playback rate is very low the audio is simply muted. | 115 |
112 AudioRendererAlgorithmBase algorithm; | 116 void TestPlaybackRate(double playback_rate) { |
113 algorithm.Initialize(kChannels, kSampleRate, kSampleBits, 0.25f, | 117 static const int kDefaultBufferSize = kSamplesPerSecond / 10; |
114 base::Bind(&base::DoNothing)); | 118 static const int kDefaultFramesRequested = 5 * kSamplesPerSecond; |
115 | 119 |
116 // First parameter is the input buffer size, second parameter is how much data | 120 TestPlaybackRate(playback_rate, kDefaultBufferSize, |
117 // we expect to consume in order to have no data left in the |algorithm|. | 121 kDefaultFramesRequested); |
118 // | 122 } |
119 // For rate == 0.25f, reading four times the input size should consume all | 123 |
120 // enqueued data but without executing OLA. | 124 void TestPlaybackRate(double playback_rate, |
121 const size_t kBufferSize = 16 * 1024; | 125 int buffer_size_in_frames, |
122 scoped_array<uint8> data(new uint8[kBufferSize]); | 126 int total_frames_requested) { |
123 const size_t kTestData[][2] = { | 127 int initial_bytes_enqueued = bytes_enqueued_; |
124 { algorithm.window_size(), algorithm.window_size() * 4}, | 128 int initial_bytes_buffered = algorithm_.bytes_buffered(); |
125 { algorithm.window_size() / 2, algorithm.window_size() * 2}, | 129 |
126 { 1u, 4u }, | 130 algorithm_.SetPlaybackRate(static_cast<float>(playback_rate)); |
127 { 0u, 0u }, | 131 |
128 }; | 132 scoped_array<uint8> buffer( |
129 | 133 new uint8[buffer_size_in_frames * algorithm_.bytes_per_frame()]); |
130 for (size_t i = 0u; i < arraysize(kTestData); ++i) { | 134 |
131 const size_t kDataSize = kTestData[i][0]; | 135 if (playback_rate == 0.0) { |
132 algorithm.EnqueueBuffer(new DataBuffer(scoped_array<uint8>( | 136 int frames_written = |
133 new uint8[kDataSize]), kDataSize)); | 137 algorithm_.FillBuffer(buffer.get(), buffer_size_in_frames); |
134 EXPECT_EQ(kDataSize, algorithm.bytes_buffered()); | 138 EXPECT_EQ(0, frames_written); |
135 | 139 return; |
136 const size_t kExpectedSize = kTestData[i][1]; | 140 } |
137 ASSERT_LE(kExpectedSize, kBufferSize); | 141 |
138 EXPECT_EQ(kExpectedSize, algorithm.FillBuffer(data.get(), kBufferSize)); | 142 int frames_remaining = total_frames_requested; |
139 EXPECT_EQ(0u, algorithm.bytes_buffered()); | 143 while (frames_remaining > 0) { |
140 } | 144 int frames_requested = std::min(buffer_size_in_frames, frames_remaining); |
| 145 int frames_written = |
| 146 algorithm_.FillBuffer(buffer.get(), frames_requested); |
| 147 CHECK_GT(frames_written, 0); |
| 148 CheckFakeData(buffer.get(), frames_written, playback_rate); |
| 149 frames_remaining -= frames_written; |
| 150 } |
| 151 |
| 152 int bytes_requested = total_frames_requested * algorithm_.bytes_per_frame(); |
| 153 int bytes_consumed = ComputeConsumedBytes(initial_bytes_enqueued, |
| 154 initial_bytes_buffered); |
| 155 |
| 156 // If playing back at normal speed, we should always get back the same |
| 157 // number of bytes requested. |
| 158 if (playback_rate == 1.0) { |
| 159 EXPECT_EQ(bytes_requested, bytes_consumed); |
| 160 return; |
| 161 } |
| 162 |
| 163 // Otherwise, allow |kMaxAcceptableDelta| difference between the target and |
| 164 // actual playback rate. |
| 165 // When |kSamplesPerSecond| and |total_frames_requested| are reasonably |
| 166 // large, one can expect less than a 1% difference in most cases. In our |
| 167 // current implementation, sped up playback is less accurate than slowed |
| 168 // down playback, and for playback_rate > 1, playback rate generally gets |
| 169 // less and less accurate the farther it drifts from 1 (though this is |
| 170 // nonlinear). |
| 171 static const double kMaxAcceptableDelta = 0.01; |
| 172 double actual_playback_rate = 1.0 * bytes_consumed / bytes_requested; |
| 173 |
| 174 // Calculate the percentage difference from the target |playback_rate| as a |
| 175 // fraction from 0.0 to 1.0. |
| 176 double delta = std::abs(1.0 - (actual_playback_rate / playback_rate)); |
| 177 |
| 178 EXPECT_LE(delta, kMaxAcceptableDelta); |
| 179 } |
| 180 |
| 181 protected: |
| 182 AudioRendererAlgorithmBase algorithm_; |
| 183 int bytes_enqueued_; |
| 184 }; |
| 185 |
| 186 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_NormalRate) { |
| 187 Initialize(); |
| 188 TestPlaybackRate(1.0); |
| 189 } |
| 190 |
| 191 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_OneAndAQuarterRate) { |
| 192 Initialize(); |
| 193 TestPlaybackRate(1.25); |
| 194 } |
| 195 |
| 196 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_OneAndAHalfRate) { |
| 197 Initialize(); |
| 198 TestPlaybackRate(1.5); |
| 199 } |
| 200 |
| 201 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_DoubleRate) { |
| 202 Initialize(); |
| 203 TestPlaybackRate(2.0); |
| 204 } |
| 205 |
| 206 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_EightTimesRate) { |
| 207 Initialize(); |
| 208 TestPlaybackRate(8.0); |
| 209 } |
| 210 |
| 211 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_ThreeQuartersRate) { |
| 212 Initialize(); |
| 213 TestPlaybackRate(0.75); |
| 214 } |
| 215 |
| 216 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_HalfRate) { |
| 217 Initialize(); |
| 218 TestPlaybackRate(0.5); |
| 219 } |
| 220 |
| 221 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_QuarterRate) { |
| 222 Initialize(); |
| 223 TestPlaybackRate(0.25); |
| 224 } |
| 225 |
| 226 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_Pause) { |
| 227 Initialize(); |
| 228 TestPlaybackRate(0.0); |
| 229 } |
| 230 |
| 231 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_SlowDown) { |
| 232 Initialize(); |
| 233 TestPlaybackRate(4.5); |
| 234 TestPlaybackRate(3.0); |
| 235 TestPlaybackRate(2.0); |
| 236 TestPlaybackRate(1.0); |
| 237 TestPlaybackRate(0.5); |
| 238 TestPlaybackRate(0.25); |
| 239 } |
| 240 |
| 241 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_SpeedUp) { |
| 242 Initialize(); |
| 243 TestPlaybackRate(0.25); |
| 244 TestPlaybackRate(0.5); |
| 245 TestPlaybackRate(1.0); |
| 246 TestPlaybackRate(2.0); |
| 247 TestPlaybackRate(3.0); |
| 248 TestPlaybackRate(4.5); |
| 249 } |
| 250 |
| 251 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_JumpAroundSpeeds) { |
| 252 Initialize(); |
| 253 TestPlaybackRate(2.1); |
| 254 TestPlaybackRate(0.9); |
| 255 TestPlaybackRate(0.6); |
| 256 TestPlaybackRate(1.4); |
| 257 TestPlaybackRate(0.3); |
| 258 } |
| 259 |
| 260 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_SmallBufferSize) { |
| 261 Initialize(); |
| 262 static const int kBufferSizeInFrames = 1; |
| 263 static const int kFramesRequested = 2 * kSamplesPerSecond; |
| 264 TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested); |
| 265 TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested); |
| 266 TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested); |
| 267 } |
| 268 |
| 269 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_LowerQualityAudio) { |
| 270 static const int kChannels = 1; |
| 271 static const int kSampleBits = 8; |
| 272 Initialize(kChannels, kSampleBits); |
| 273 TestPlaybackRate(1.0); |
| 274 TestPlaybackRate(0.5); |
| 275 TestPlaybackRate(1.5); |
| 276 } |
| 277 |
| 278 TEST_F(AudioRendererAlgorithmBaseTest, FillBuffer_HigherQualityAudio) { |
| 279 static const int kChannels = 2; |
| 280 static const int kSampleBits = 32; |
| 281 Initialize(kChannels, kSampleBits); |
| 282 TestPlaybackRate(1.0); |
| 283 TestPlaybackRate(0.5); |
| 284 TestPlaybackRate(1.5); |
141 } | 285 } |
142 | 286 |
143 } // namespace media | 287 } // namespace media |
OLD | NEW |