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

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

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

Powered by Google App Engine
This is Rietveld 408576698