Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(596)

Side by Side Diff: media/base/audio_rechunker_unittest.cc

Issue 1714593003: Introduce media::AudioPushFifo and a couple of use cases (and clean-ups). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix Win compile issue. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 <limits>
6 #include <vector>
7
8 #include "base/bind.h"
9 #include "base/bind_helpers.h"
10 #include "base/macros.h"
11 #include "media/base/audio_bus.h"
12 #include "media/base/audio_rechunker.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace media {
16
17 namespace {
18
19 struct TestParams {
20 int64_t duration_us;
21 int sample_rate;
22 int output_chunk_size;
23 bool expect_evenly_divided;
24 TestParams(int64_t d, int s, int e, bool e2)
25 : duration_us(d),
26 sample_rate(s),
27 output_chunk_size(e),
28 expect_evenly_divided(e2) {}
29 };
30
31 class AudioRechunkerTest : public testing::TestWithParam<TestParams> {
32 public:
33 AudioRechunkerTest() {}
34 ~AudioRechunkerTest() override {}
35
36 void SetUp() final {
37 rechunker_.reset(new AudioRechunker(
38 output_duration(),
39 base::Bind(&AudioRechunkerTest::ReceiveAndCheckNextChunk,
40 base::Unretained(this))));
41 ASSERT_EQ(GetParam().expect_evenly_divided,
42 rechunker_->SetSampleRate(GetParam().sample_rate));
43 ASSERT_EQ(GetParam().output_chunk_size, rechunker_->output_frames());
44 }
45
46 protected:
47 struct OutputChunkResult {
48 int num_frames;
49 base::TimeDelta reference_timestamp;
50 float first_sample_value;
51 float last_sample_value;
52 };
53
54 base::TimeDelta output_duration() const {
55 return base::TimeDelta::FromMicroseconds(GetParam().duration_us);
56 }
57
58 // Returns the number of output chunks that should have been emitted given the
59 // number of input frames pushed so far.
60 size_t GetExpectedOutputChunks(int frames_pushed) const {
61 return static_cast<size_t>(frames_pushed / GetParam().output_chunk_size);
62 }
63
64 // Returns the number of Push() calls to make in order to get at least 3
65 // output chunks.
66 int GetNumPushTestIterations(int input_chunk_size) const {
67 return 3 * std::max(1, GetParam().output_chunk_size / input_chunk_size);
68 }
69
70 // Repeatedly pushes constant-sized batches of input samples and checks that
71 // the input data is re-chunked correctly.
72 void RunSimpleRechunkTest(int input_chunk_size) {
73 const int num_iterations = GetNumPushTestIterations(input_chunk_size);
74
75 int sample_value = 0;
76 const scoped_ptr<AudioBus> audio_bus =
77 AudioBus::Create(1, input_chunk_size);
78
79 for (int i = 0; i < num_iterations; ++i) {
80 EXPECT_EQ(GetExpectedOutputChunks(i * input_chunk_size), results_.size());
81
82 // Fill audio data with predictable values.
83 for (int j = 0; j < audio_bus->frames(); ++j)
84 audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
85
86 rechunker_->Push(*audio_bus, base::TimeDelta::FromMicroseconds(
87 i * input_chunk_size * INT64_C(1000000) /
88 GetParam().sample_rate));
89 // Note: Rechunker has just called ReceiveAndCheckNextChunk() zero or more
90 // times.
91 }
92 EXPECT_EQ(GetExpectedOutputChunks(num_iterations * input_chunk_size),
93 results_.size());
94
95 ASSERT_FALSE(results_.empty());
96 EXPECT_EQ(0.0f, results_.front().first_sample_value);
97 const float last_value_in_last_chunk = static_cast<float>(
98 GetExpectedOutputChunks(num_iterations * input_chunk_size) *
99 GetParam().output_chunk_size -
100 1);
101 EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
102 }
103
104 // Returns a "random" integer in the range [begin,end).
105 int GetRandomInRange(int begin, int end) {
106 const int len = end - begin;
107 const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
108 return begin + rand_offset;
109 }
110
111 scoped_ptr<AudioRechunker> rechunker_;
112 std::vector<OutputChunkResult> results_;
113
114 private:
115 // Called by |rechunker_| to deliver another chunk of audio. Sanity checks
116 // the sample values are as expected, and without any dropped/duplicated, and
117 // adds a result to |results_|.
118 void ReceiveAndCheckNextChunk(const AudioBus& audio_bus,
119 base::TimeDelta reference_timestamp) {
120 OutputChunkResult result;
121 result.num_frames = audio_bus.frames();
122 result.reference_timestamp = reference_timestamp;
123 result.first_sample_value = audio_bus.channel(0)[0];
124 result.last_sample_value = audio_bus.channel(0)[audio_bus.frames() - 1];
125
126 // Check that each sample value is the previous sample value plus one.
127 for (int i = 1; i < audio_bus.frames(); ++i) {
128 ASSERT_EQ(result.first_sample_value + i, audio_bus.channel(0)[i])
129 << "Sample at offset " << i << " is incorrect.";
130 }
131
132 // Check that no audio samples were dropped, and that the reference
133 // timestamps are monotonically increasing.
134 if (!results_.empty()) {
135 const OutputChunkResult& last_result = results_.back();
136 ASSERT_EQ(last_result.last_sample_value + 1, result.first_sample_value);
137 ASSERT_LT(last_result.reference_timestamp, result.reference_timestamp);
138 }
139
140 results_.push_back(result);
141 }
142
143 // Note: Not using base::RandInt() because it is horribly slow on debug
144 // builds. The following is a very simple, deterministic LCG:
145 int NextRandomInt() {
146 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
147 return static_cast<int>(rand_seed_);
148 }
149
150 uint32_t rand_seed_ = 0x7e110;
151
152 DISALLOW_COPY_AND_ASSIGN(AudioRechunkerTest);
153 };
154
155 // Tests an atypical edge case: Push()ing one frame at a time.
156 TEST_P(AudioRechunkerTest, PushOneFrameAtATime) {
157 RunSimpleRechunkTest(1);
158 }
159
160 // Tests that re-chunking the audio from common platform input chunk sizes
161 // works.
162 TEST_P(AudioRechunkerTest, Push128FramesAtATime) {
163 RunSimpleRechunkTest(128);
164 }
165 TEST_P(AudioRechunkerTest, Push512FramesAtATime) {
166 RunSimpleRechunkTest(512);
167 }
168
169 // Tests that re-chunking the audio from common "10 ms" input chunk sizes
170 // works (44100 Hz * 10 ms = 441, and 48000 Hz * 10 ms = 480).
171 TEST_P(AudioRechunkerTest, Push441FramesAtATime) {
172 RunSimpleRechunkTest(441);
173 }
174 TEST_P(AudioRechunkerTest, Push480FramesAtATime) {
175 RunSimpleRechunkTest(480);
176 }
177
178 // Tests that re-chunking when input audio is provided in varying chunk sizes
179 // works.
180 TEST_P(AudioRechunkerTest, PushArbitraryNumbersOfFramesAtATime) {
181 // The loop below will run until both: 1) kMinNumIterations loops have
182 // occurred; and 2) there are at least 3 entries in |results_|.
183 const int kMinNumIterations = 30;
184
185 int sample_value = 0;
186 int frames_pushed_so_far = 0;
187 for (int i = 0; i < kMinNumIterations || results_.size() < 3; ++i) {
188 EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
189
190 // Create an AudioBus of a random length, populated with sample values.
191 const int input_chunk_size = GetRandomInRange(1, 1920);
192 const scoped_ptr<AudioBus> audio_bus =
193 AudioBus::Create(1, input_chunk_size);
194 for (int j = 0; j < audio_bus->frames(); ++j)
195 audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
196
197 rechunker_->Push(*audio_bus, base::TimeDelta::FromMicroseconds(
198 frames_pushed_so_far * INT64_C(1000000) /
199 GetParam().sample_rate));
200 // Note: Rechunker has just called ReceiveAndCheckNextChunk() zero or more
201 // times.
202
203 frames_pushed_so_far += input_chunk_size;
204 }
205 EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
206
207 ASSERT_FALSE(results_.empty());
208 EXPECT_EQ(0.0f, results_.front().first_sample_value);
209 const float last_value_in_last_chunk =
210 static_cast<float>(GetExpectedOutputChunks(frames_pushed_so_far) *
211 GetParam().output_chunk_size -
212 1);
213 EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
214 }
215
216 INSTANTIATE_TEST_CASE_P(,
217 AudioRechunkerTest,
218 ::testing::Values(
219 // 1 ms output chunks at common sample rates.
220 TestParams(1000, 16000, 16, true),
221 TestParams(1000, 22050, 22, false),
222 TestParams(1000, 44100, 44, false),
223 TestParams(1000, 48000, 48, true),
224
225 // 10 ms output chunks at common sample rates.
226 TestParams(10000, 16000, 160, true),
227 TestParams(10000, 22050, 220, false),
228 TestParams(10000, 44100, 441, true),
229 TestParams(10000, 48000, 480, true),
230
231 // 60 ms output chunks at common sample rates.
232 TestParams(60000, 16000, 960, true),
233 TestParams(60000, 22050, 1323, true),
234 TestParams(60000, 44100, 2646, true),
235 TestParams(60000, 48000, 2880, true)));
236
237 } // namespace
238
239 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698