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

Side by Side Diff: media/base/audio_push_fifo_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 unittest compile breakage caused by recent method rename. 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
« no previous file with comments | « media/base/audio_push_fifo.cc ('k') | media/media.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_push_fifo.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace media {
16
17 namespace {
18
19 class AudioPushFifoTest : public testing::TestWithParam<int> {
20 public:
21 AudioPushFifoTest() {}
22 ~AudioPushFifoTest() override {}
23
24 int output_chunk_size() const { return GetParam(); }
25
26 void SetUp() final {
27 fifo_.reset(new AudioPushFifo(base::Bind(
28 &AudioPushFifoTest::ReceiveAndCheckNextChunk, base::Unretained(this))));
29 fifo_->Reset(output_chunk_size());
30 ASSERT_EQ(output_chunk_size(), fifo_->frames_per_buffer());
31 }
32
33 protected:
34 struct OutputChunkResult {
35 int num_frames;
36 float first_sample_value;
37 float last_sample_value;
38 int frame_delay;
39 };
40
41 // Returns the number of output chunks that should have been emitted given the
42 // number of input frames pushed so far.
43 size_t GetExpectedOutputChunks(int frames_pushed) const {
44 return static_cast<size_t>(frames_pushed / output_chunk_size());
45 }
46
47 // Returns the number of Push() calls to make in order to get at least 3
48 // output chunks.
49 int GetNumPushTestIterations(int input_chunk_size) const {
50 return 3 * std::max(1, output_chunk_size() / input_chunk_size);
51 }
52
53 // Repeatedly pushes constant-sized batches of input samples and checks that
54 // the input data is re-chunked correctly.
55 void RunSimpleRechunkTest(int input_chunk_size) {
56 const int num_iterations = GetNumPushTestIterations(input_chunk_size);
57
58 int sample_value = 0;
59 const scoped_ptr<AudioBus> audio_bus =
60 AudioBus::Create(1, input_chunk_size);
61
62 for (int i = 0; i < num_iterations; ++i) {
63 EXPECT_EQ(GetExpectedOutputChunks(i * input_chunk_size), results_.size());
64
65 // Fill audio data with predictable values.
66 for (int j = 0; j < audio_bus->frames(); ++j)
67 audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
68
69 fifo_->Push(*audio_bus);
70 // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or
71 // more times.
72 }
73 EXPECT_EQ(GetExpectedOutputChunks(num_iterations * input_chunk_size),
74 results_.size());
75
76 // Confirm first and last sample values that have been output are the
77 // expected ones.
78 ASSERT_FALSE(results_.empty());
79 EXPECT_EQ(0.0f, results_.front().first_sample_value);
80 const float last_value_in_last_chunk = static_cast<float>(
81 GetExpectedOutputChunks(num_iterations * input_chunk_size) *
82 output_chunk_size() -
83 1);
84 EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
85
86 // Confirm the expected frame delays for the first output chunk (or two).
87 if (input_chunk_size < output_chunk_size()) {
88 const int num_queued_before_first_output =
89 ((output_chunk_size() - 1) / input_chunk_size) * input_chunk_size;
90 EXPECT_EQ(-num_queued_before_first_output, results_.front().frame_delay);
91 } else if (input_chunk_size >= output_chunk_size()) {
92 EXPECT_EQ(0, results_[0].frame_delay);
93 if (input_chunk_size >= 2 * output_chunk_size()) {
94 EXPECT_EQ(output_chunk_size(), results_[1].frame_delay);
95 } else {
96 const int num_remaining_after_first_output =
97 input_chunk_size - output_chunk_size();
98 EXPECT_EQ(-num_remaining_after_first_output, results_[1].frame_delay);
99 }
100 }
101
102 const size_t num_results_before_flush = results_.size();
103 fifo_->Flush();
104 const size_t num_results_after_flush = results_.size();
105 if (num_results_after_flush > num_results_before_flush) {
106 EXPECT_NE(0, results_.back().frame_delay);
107 EXPECT_LT(-output_chunk_size(), results_.back().frame_delay);
108 }
109 }
110
111 // Returns a "random" integer in the range [begin,end).
112 int GetRandomInRange(int begin, int end) {
113 const int len = end - begin;
114 const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
115 return begin + rand_offset;
116 }
117
118 scoped_ptr<AudioPushFifo> fifo_;
119 std::vector<OutputChunkResult> results_;
120
121 private:
122 // Called by |fifo_| to deliver another chunk of audio. Sanity checks
123 // the sample values are as expected, and without any dropped/duplicated, and
124 // adds a result to |results_|.
125 void ReceiveAndCheckNextChunk(const AudioBus& audio_bus, int frame_delay) {
126 OutputChunkResult result;
127 result.num_frames = audio_bus.frames();
128 result.first_sample_value = audio_bus.channel(0)[0];
129 result.last_sample_value = audio_bus.channel(0)[audio_bus.frames() - 1];
130 result.frame_delay = frame_delay;
131
132 // Check that each sample value is the previous sample value plus one.
133 for (int i = 1; i < audio_bus.frames(); ++i) {
134 const float expected_value = result.first_sample_value + i;
135 const float actual_value = audio_bus.channel(0)[i];
136 if (actual_value != expected_value) {
137 if (actual_value == 0.0f) {
138 // This chunk is probably being emitted by a Flush(). If that's true
139 // then the frame_delay will be negative and the rest of the
140 // |audio_bus| should be all zeroes.
141 ASSERT_GT(0, frame_delay);
142 for (int j = i + 1; j < audio_bus.frames(); ++j)
143 ASSERT_EQ(0.0f, audio_bus.channel(0)[j]);
144 break;
145 } else {
146 ASSERT_EQ(expected_value, actual_value) << "Sample at offset " << i
147 << " is incorrect.";
148 }
149 }
150 }
151
152 results_.push_back(result);
153 }
154
155 // Note: Not using base::RandInt() because it is horribly slow on debug
156 // builds. The following is a very simple, deterministic LCG:
157 int NextRandomInt() {
158 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
159 return static_cast<int>(rand_seed_);
160 }
161
162 uint32_t rand_seed_ = 0x7e110;
163
164 DISALLOW_COPY_AND_ASSIGN(AudioPushFifoTest);
165 };
166
167 // Tests an atypical edge case: Push()ing one frame at a time.
168 TEST_P(AudioPushFifoTest, PushOneFrameAtATime) {
169 RunSimpleRechunkTest(1);
170 }
171
172 // Tests that re-chunking the audio from common platform input chunk sizes
173 // works.
174 TEST_P(AudioPushFifoTest, Push128FramesAtATime) {
175 RunSimpleRechunkTest(128);
176 }
177 TEST_P(AudioPushFifoTest, Push512FramesAtATime) {
178 RunSimpleRechunkTest(512);
179 }
180
181 // Tests that re-chunking the audio from common "10 ms" input chunk sizes
182 // works (44100 Hz * 10 ms = 441, and 48000 Hz * 10 ms = 480).
183 TEST_P(AudioPushFifoTest, Push441FramesAtATime) {
184 RunSimpleRechunkTest(441);
185 }
186 TEST_P(AudioPushFifoTest, Push480FramesAtATime) {
187 RunSimpleRechunkTest(480);
188 }
189
190 // Tests that re-chunking when input audio is provided in varying chunk sizes
191 // works.
192 TEST_P(AudioPushFifoTest, PushArbitraryNumbersOfFramesAtATime) {
193 // The loop below will run until both: 1) kMinNumIterations loops have
194 // occurred; and 2) there are at least 3 entries in |results_|.
195 const int kMinNumIterations = 30;
196
197 int sample_value = 0;
198 int frames_pushed_so_far = 0;
199 for (int i = 0; i < kMinNumIterations || results_.size() < 3; ++i) {
200 EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
201
202 // Create an AudioBus of a random length, populated with sample values.
203 const int input_chunk_size = GetRandomInRange(1, 1920);
204 const scoped_ptr<AudioBus> audio_bus =
205 AudioBus::Create(1, input_chunk_size);
206 for (int j = 0; j < audio_bus->frames(); ++j)
207 audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
208
209 fifo_->Push(*audio_bus);
210 // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or
211 // more times.
212
213 frames_pushed_so_far += input_chunk_size;
214 }
215 EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
216
217 ASSERT_FALSE(results_.empty());
218 EXPECT_EQ(0.0f, results_.front().first_sample_value);
219 const float last_value_in_last_chunk = static_cast<float>(
220 GetExpectedOutputChunks(frames_pushed_so_far) * output_chunk_size() - 1);
221 EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
222
223 const size_t num_results_before_flush = results_.size();
224 fifo_->Flush();
225 const size_t num_results_after_flush = results_.size();
226 if (num_results_after_flush > num_results_before_flush) {
227 EXPECT_NE(0, results_.back().frame_delay);
228 EXPECT_LT(-output_chunk_size(), results_.back().frame_delay);
229 }
230 }
231
232 INSTANTIATE_TEST_CASE_P(,
233 AudioPushFifoTest,
234 ::testing::Values(
235 // 1 ms output chunks at common sample rates.
236 16, // 16000 Hz
237 22, // 22050 Hz
238 44, // 44100 Hz
239 48, // 48000 Hz
240
241 // 10 ms output chunks at common sample rates.
242 160, // 16000 Hz
243 220, // 22050 Hz
244 441, // 44100 Hz
245 480, // 48000 Hz
246
247 // 60 ms output chunks at common sample rates.
248 960, // 16000 Hz
249 1323, // 22050 Hz
250 2646, // 44100 Hz
251 2880 // 48000 Hz
252 ));
253
254 } // namespace
255
256 } // namespace media
OLDNEW
« no previous file with comments | « media/base/audio_push_fifo.cc ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698