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

Side by Side Diff: content/browser/renderer_host/media/audio_debug_file_writer_unittest.cc

Issue 2702323002: Move AudioDebugFileWriter from content/ to media/. (Closed)
Patch Set: Code review, unit test fix and rebase. Created 3 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 2015 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 <stdint.h>
6
7 #include "base/files/file_util.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/sys_byteorder.h"
11 #include "content/browser/renderer_host/media/audio_debug_file_writer.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/test/test_browser_thread_bundle.h"
14 #include "media/base/audio_bus.h"
15 #include "media/base/test_helpers.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace content {
19
20 namespace {
21
22 static const uint16_t kBytesPerSample = sizeof(uint16_t);
23 static const uint16_t kPcmEncoding = 1;
24 static const size_t kWavHeaderSize = 44;
25
26 uint16_t ReadLE2(const char* buf) {
27 return static_cast<uint8_t>(buf[0]) | (static_cast<uint8_t>(buf[1]) << 8);
28 }
29
30 uint32_t ReadLE4(const char* buf) {
31 return static_cast<uint8_t>(buf[0]) | (static_cast<uint8_t>(buf[1]) << 8) |
32 (static_cast<uint8_t>(buf[2]) << 16) |
33 (static_cast<uint8_t>(buf[3]) << 24);
34 }
35
36 } // namespace
37
38 // <channel layout, sample rate, frames per buffer, number of buffer writes
39 typedef std::tr1::tuple<media::ChannelLayout, int, int, int>
40 AudioDebugFileWriterTestData;
41
42 class AudioDebugFileWriterTest
43 : public testing::TestWithParam<AudioDebugFileWriterTestData> {
44 public:
45 AudioDebugFileWriterTest()
46 : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD),
47 params_(media::AudioParameters::Format::AUDIO_PCM_LINEAR,
48 std::tr1::get<0>(GetParam()),
49 std::tr1::get<1>(GetParam()),
50 kBytesPerSample * 8,
51 std::tr1::get<2>(GetParam())),
52 writes_(std::tr1::get<3>(GetParam())),
53 source_samples_(params_.frames_per_buffer() * params_.channels() *
54 writes_),
55 source_interleaved_(source_samples_ ? new int16_t[source_samples_]
56 : nullptr) {
57 InitSourceInterleaved(source_interleaved_.get(), source_samples_);
58 }
59
60 protected:
61 virtual ~AudioDebugFileWriterTest() {}
62
63 static void InitSourceInterleaved(int16_t* source_interleaved,
64 int source_samples) {
65 if (source_samples) {
66 // equal steps to cover int16_t range of values
67 int16_t step = 0xffff / source_samples;
68 int16_t val = std::numeric_limits<int16_t>::min();
69 for (int i = 0; i < source_samples; ++i, val += step)
70 source_interleaved[i] = val;
71 }
72 }
73
74 static void VerifyHeader(const char(&wav_header)[kWavHeaderSize],
75 const media::AudioParameters& params,
76 int writes,
77 int64_t file_length) {
78 uint32_t block_align = params.channels() * kBytesPerSample;
79 uint32_t data_size =
80 static_cast<uint32_t>(params.frames_per_buffer() * params.channels() *
81 writes * kBytesPerSample);
82 // Offset Length Content
83 // 0 4 "RIFF"
84 EXPECT_EQ(0, strncmp(wav_header, "RIFF", 4));
85 // 4 4 <file length - 8>
86 ASSERT_GT(file_length, 8);
87 EXPECT_EQ(static_cast<uint64_t>(file_length - 8), ReadLE4(wav_header + 4));
88 EXPECT_EQ(static_cast<uint32_t>(data_size + kWavHeaderSize - 8),
89 ReadLE4(wav_header + 4));
90 // 8 4 "WAVE"
91 // 12 4 "fmt "
92 EXPECT_EQ(0, strncmp(wav_header + 8, "WAVEfmt ", 8));
93 // 16 4 <length of the fmt data> (=16)
94 EXPECT_EQ(16U, ReadLE4(wav_header + 16));
95 // 20 2 <WAVE file encoding tag>
96 EXPECT_EQ(kPcmEncoding, ReadLE2(wav_header + 20));
97 // 22 2 <channels>
98 EXPECT_EQ(params.channels(), ReadLE2(wav_header + 22));
99 // 24 4 <sample rate>
100 EXPECT_EQ(static_cast<uint32_t>(params.sample_rate()),
101 ReadLE4(wav_header + 24));
102 // 28 4 <bytes per second> (sample rate * block align)
103 EXPECT_EQ(static_cast<uint32_t>(params.sample_rate()) * block_align,
104 ReadLE4(wav_header + 28));
105 // 32 2 <block align> (channels * bits per sample / 8)
106 EXPECT_EQ(block_align, ReadLE2(wav_header + 32));
107 // 34 2 <bits per sample>
108 EXPECT_EQ(kBytesPerSample * 8, ReadLE2(wav_header + 34));
109 // 36 4 "data"
110 EXPECT_EQ(0, strncmp(wav_header + 36, "data", 4));
111 // 40 4 <sample data size(n)>
112 EXPECT_EQ(data_size, ReadLE4(wav_header + 40));
113 }
114
115 // |result_interleaved| is expected to be little-endian.
116 static void VerifyDataRecording(const int16_t* source_interleaved,
117 const int16_t* result_interleaved,
118 int16_t source_samples) {
119 // Allow mismatch by 1 due to rounding error in int->float->int
120 // calculations.
121 for (int i = 0; i < source_samples; ++i)
122 EXPECT_LE(std::abs(static_cast<int16_t>(
123 base::ByteSwapToLE16(source_interleaved[i])) -
124 result_interleaved[i]),
125 1)
126 << "i = " << i << " source " << source_interleaved[i] << " result "
127 << result_interleaved[i];
128 }
129
130 void VerifyRecording(const base::FilePath& file_path) {
131 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
132 EXPECT_TRUE(file.IsValid());
133
134 char wav_header[kWavHeaderSize];
135 EXPECT_EQ(file.Read(0, wav_header, kWavHeaderSize),
136 static_cast<int>(kWavHeaderSize));
137 VerifyHeader(wav_header, params_, writes_, file.GetLength());
138
139 if (source_samples_ > 0) {
140 std::unique_ptr<int16_t[]> result_interleaved(
141 new int16_t[source_samples_]);
142 memset(result_interleaved.get(), 0, source_samples_ * kBytesPerSample);
143
144 // Recording is read from file as a byte sequence, so it stored as
145 // little-endian.
146 int read = file.Read(kWavHeaderSize,
147 reinterpret_cast<char*>(result_interleaved.get()),
148 source_samples_ * kBytesPerSample);
149 EXPECT_EQ(static_cast<int>(file.GetLength() - kWavHeaderSize), read);
150
151 VerifyDataRecording(source_interleaved_.get(), result_interleaved.get(),
152 source_samples_);
153 }
154 }
155
156 void TestDoneOnFileThread(const base::Closure& callback) {
157 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
158
159 callback.Run();
160 }
161
162 void DoDebugRecording() {
163 // Write tasks are posted to BrowserThread::FILE.
164 for (int i = 0; i < writes_; ++i) {
165 std::unique_ptr<media::AudioBus> bus = media::AudioBus::Create(
166 params_.channels(), params_.frames_per_buffer());
167
168 bus->FromInterleaved(
169 source_interleaved_.get() +
170 i * params_.channels() * params_.frames_per_buffer(),
171 params_.frames_per_buffer(), kBytesPerSample);
172
173 input_debug_writer_->Write(std::move(bus));
174 }
175 }
176
177 void WaitForRecordingCompletion() {
178 media::WaitableMessageLoopEvent event;
179
180 // Post a task to BrowserThread::FILE indicating that all the writes are
181 // done.
182 BrowserThread::PostTask(
183 BrowserThread::FILE, FROM_HERE,
184 base::Bind(&AudioDebugFileWriterTest::TestDoneOnFileThread,
185 base::Unretained(this), event.GetClosure()));
186
187 // Wait for TestDoneOnFileThread() to call event's closure.
188 event.RunAndWait();
189 }
190
191 void RecordAndVerifyOnce() {
192 base::FilePath file_path;
193 EXPECT_TRUE(base::CreateTemporaryFile(&file_path));
194
195 input_debug_writer_->Start(file_path);
196
197 DoDebugRecording();
198
199 input_debug_writer_->Stop();
200
201 WaitForRecordingCompletion();
202
203 VerifyRecording(file_path);
204
205 if (::testing::Test::HasFailure()) {
206 LOG(ERROR) << "Test failed; keeping recording(s) at ["
207 << file_path.value().c_str() << "].";
208 } else {
209 EXPECT_TRUE(base::DeleteFile(file_path, false));
210 }
211 }
212
213 protected:
214 TestBrowserThreadBundle thread_bundle_;
215
216 // Writer under test.
217 std::unique_ptr<AudioDebugFileWriter> input_debug_writer_;
218
219 // AudioBus parameters.
220 media::AudioParameters params_;
221
222 // Number of times to write AudioBus to the file.
223 int writes_;
224
225 // Number of samples in the source data.
226 int source_samples_;
227
228 // Source data.
229 std::unique_ptr<int16_t[]> source_interleaved_;
230
231 private:
232 DISALLOW_COPY_AND_ASSIGN(AudioDebugFileWriterTest);
233 };
234
235 class AudioDebugFileWriterBehavioralTest : public AudioDebugFileWriterTest {};
236
237 TEST_P(AudioDebugFileWriterTest, WaveRecordingTest) {
238 input_debug_writer_.reset(new AudioDebugFileWriter(params_));
239
240 RecordAndVerifyOnce();
241 }
242
243 TEST_P(AudioDebugFileWriterBehavioralTest,
244 DeletedBeforeRecordingFinishedOnFileThread) {
245 input_debug_writer_.reset(new AudioDebugFileWriter(params_));
246
247 base::FilePath file_path;
248 EXPECT_TRUE(base::CreateTemporaryFile(&file_path));
249
250 base::WaitableEvent* wait_for_deletion =
251 new base::WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL,
252 base::WaitableEvent::InitialState::NOT_SIGNALED);
253
254 BrowserThread::PostTask(
255 BrowserThread::FILE, FROM_HERE,
256 base::Bind(&base::WaitableEvent::Wait, base::Owned(wait_for_deletion)));
257
258 input_debug_writer_->Start(file_path);
259
260 DoDebugRecording();
261
262 input_debug_writer_.reset();
263 wait_for_deletion->Signal();
264
265 WaitForRecordingCompletion();
266
267 VerifyRecording(file_path);
268
269 if (::testing::Test::HasFailure()) {
270 LOG(ERROR) << "Test failed; keeping recording(s) at ["
271 << file_path.value().c_str() << "].";
272 } else {
273 EXPECT_TRUE(base::DeleteFile(file_path, false));
274 }
275 }
276
277 TEST_P(AudioDebugFileWriterBehavioralTest, FileCreationError) {
278 input_debug_writer_.reset(new AudioDebugFileWriter(params_));
279 base::FilePath file_path; // Empty file name.
280 input_debug_writer_->Start(file_path);
281 DoDebugRecording();
282 }
283
284 TEST_P(AudioDebugFileWriterBehavioralTest, StartStopStartStop) {
285 input_debug_writer_.reset(new AudioDebugFileWriter(params_));
286 RecordAndVerifyOnce();
287 RecordAndVerifyOnce();
288 }
289
290 TEST_P(AudioDebugFileWriterBehavioralTest, DestroyNotStarted) {
291 input_debug_writer_.reset(new AudioDebugFileWriter(params_));
292 input_debug_writer_.reset();
293 }
294
295 TEST_P(AudioDebugFileWriterBehavioralTest, DestroyStarted) {
296 input_debug_writer_.reset(new AudioDebugFileWriter(params_));
297 base::FilePath file_path;
298 EXPECT_TRUE(base::CreateTemporaryFile(&file_path));
299 input_debug_writer_->Start(file_path);
300 input_debug_writer_.reset();
301 }
302
303 INSTANTIATE_TEST_CASE_P(
304 AudioDebugFileWriterTest,
305 AudioDebugFileWriterTest,
306 // Using 10ms frames per buffer everywhere.
307 testing::Values(
308 // No writes.
309 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO,
310 44100,
311 44100 / 100,
312 0),
313 // 1 write of mono.
314 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO,
315 44100,
316 44100 / 100,
317 1),
318 // 1 second of mono.
319 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO,
320 44100,
321 44100 / 100,
322 100),
323 // 1 second of mono, higher rate.
324 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO,
325 48000,
326 48000 / 100,
327 100),
328 // 1 second of stereo.
329 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_STEREO,
330 44100,
331 44100 / 100,
332 100),
333 // 15 seconds of stereo, higher rate.
334 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_STEREO,
335 48000,
336 48000 / 100,
337 1500)));
338
339 INSTANTIATE_TEST_CASE_P(
340 AudioDebugFileWriterBehavioralTest,
341 AudioDebugFileWriterBehavioralTest,
342 // Using 10ms frames per buffer everywhere.
343 testing::Values(
344 // No writes.
345 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO,
346 44100,
347 44100 / 100,
348 100)));
349
350 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698