OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 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 #include <stdint.h> | 5 #include <stdint.h> |
6 | 6 |
| 7 #include "base/bind.h" |
7 #include "base/files/file_util.h" | 8 #include "base/files/file_util.h" |
8 #include "base/memory/ptr_util.h" | 9 #include "base/memory/ptr_util.h" |
| 10 #include "base/message_loop/message_loop.h" |
9 #include "base/synchronization/waitable_event.h" | 11 #include "base/synchronization/waitable_event.h" |
10 #include "base/sys_byteorder.h" | 12 #include "base/sys_byteorder.h" |
11 #include "content/browser/renderer_host/media/audio_debug_file_writer.h" | 13 #include "base/threading/thread.h" |
12 #include "content/public/browser/browser_thread.h" | 14 #include "media/audio/audio_debug_file_writer.h" |
13 #include "content/public/test/test_browser_thread_bundle.h" | |
14 #include "media/base/audio_bus.h" | 15 #include "media/base/audio_bus.h" |
15 #include "media/base/test_helpers.h" | 16 #include "media/base/test_helpers.h" |
16 #include "testing/gtest/include/gtest/gtest.h" | 17 #include "testing/gtest/include/gtest/gtest.h" |
17 | 18 |
18 namespace content { | 19 namespace media { |
19 | 20 |
20 namespace { | 21 namespace { |
21 | 22 |
22 static const uint16_t kBytesPerSample = sizeof(uint16_t); | 23 static const uint16_t kBytesPerSample = sizeof(uint16_t); |
23 static const uint16_t kPcmEncoding = 1; | 24 static const uint16_t kPcmEncoding = 1; |
24 static const size_t kWavHeaderSize = 44; | 25 static const size_t kWavHeaderSize = 44; |
25 | 26 |
26 uint16_t ReadLE2(const char* buf) { | 27 uint16_t ReadLE2(const char* buf) { |
27 return static_cast<uint8_t>(buf[0]) | (static_cast<uint8_t>(buf[1]) << 8); | 28 return static_cast<uint8_t>(buf[0]) | (static_cast<uint8_t>(buf[1]) << 8); |
28 } | 29 } |
29 | 30 |
30 uint32_t ReadLE4(const char* buf) { | 31 uint32_t ReadLE4(const char* buf) { |
31 return static_cast<uint8_t>(buf[0]) | (static_cast<uint8_t>(buf[1]) << 8) | | 32 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[2]) << 16) | |
33 (static_cast<uint8_t>(buf[3]) << 24); | 34 (static_cast<uint8_t>(buf[3]) << 24); |
34 } | 35 } |
35 | 36 |
36 } // namespace | 37 } // namespace |
37 | 38 |
38 // <channel layout, sample rate, frames per buffer, number of buffer writes | 39 // <channel layout, sample rate, frames per buffer, number of buffer writes |
39 typedef std::tr1::tuple<media::ChannelLayout, int, int, int> | 40 typedef std::tr1::tuple<ChannelLayout, int, int, int> |
40 AudioDebugFileWriterTestData; | 41 AudioDebugFileWriterTestData; |
41 | 42 |
42 class AudioDebugFileWriterTest | 43 class AudioDebugFileWriterTest |
43 : public testing::TestWithParam<AudioDebugFileWriterTestData> { | 44 : public testing::TestWithParam<AudioDebugFileWriterTestData> { |
44 public: | 45 public: |
45 AudioDebugFileWriterTest() | 46 AudioDebugFileWriterTest() |
46 : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD), | 47 : file_thread_("FileThread"), |
47 params_(media::AudioParameters::Format::AUDIO_PCM_LINEAR, | 48 params_(AudioParameters::Format::AUDIO_PCM_LINEAR, |
48 std::tr1::get<0>(GetParam()), | 49 std::tr1::get<0>(GetParam()), |
49 std::tr1::get<1>(GetParam()), | 50 std::tr1::get<1>(GetParam()), |
50 kBytesPerSample * 8, | 51 kBytesPerSample * 8, |
51 std::tr1::get<2>(GetParam())), | 52 std::tr1::get<2>(GetParam())), |
52 writes_(std::tr1::get<3>(GetParam())), | 53 writes_(std::tr1::get<3>(GetParam())), |
53 source_samples_(params_.frames_per_buffer() * params_.channels() * | 54 source_samples_(params_.frames_per_buffer() * params_.channels() * |
54 writes_), | 55 writes_), |
55 source_interleaved_(source_samples_ ? new int16_t[source_samples_] | 56 source_interleaved_(source_samples_ ? new int16_t[source_samples_] |
56 : nullptr) { | 57 : nullptr) { |
| 58 file_thread_.StartAndWaitForTesting(); |
57 InitSourceInterleaved(source_interleaved_.get(), source_samples_); | 59 InitSourceInterleaved(source_interleaved_.get(), source_samples_); |
58 } | 60 } |
59 | 61 |
60 protected: | 62 protected: |
61 virtual ~AudioDebugFileWriterTest() {} | 63 virtual ~AudioDebugFileWriterTest() { file_thread_.Stop(); } |
62 | 64 |
63 static void InitSourceInterleaved(int16_t* source_interleaved, | 65 static void InitSourceInterleaved(int16_t* source_interleaved, |
64 int source_samples) { | 66 int source_samples) { |
65 if (source_samples) { | 67 if (source_samples) { |
66 // equal steps to cover int16_t range of values | 68 // equal steps to cover int16_t range of values |
67 int16_t step = 0xffff / source_samples; | 69 int16_t step = 0xffff / source_samples; |
68 int16_t val = std::numeric_limits<int16_t>::min(); | 70 int16_t val = std::numeric_limits<int16_t>::min(); |
69 for (int i = 0; i < source_samples; ++i, val += step) | 71 for (int i = 0; i < source_samples; ++i, val += step) |
70 source_interleaved[i] = val; | 72 source_interleaved[i] = val; |
71 } | 73 } |
72 } | 74 } |
73 | 75 |
74 static void VerifyHeader(const char(&wav_header)[kWavHeaderSize], | 76 static void VerifyHeader(const char (&wav_header)[kWavHeaderSize], |
75 const media::AudioParameters& params, | 77 const AudioParameters& params, |
76 int writes, | 78 int writes, |
77 int64_t file_length) { | 79 int64_t file_length) { |
78 uint32_t block_align = params.channels() * kBytesPerSample; | 80 uint32_t block_align = params.channels() * kBytesPerSample; |
79 uint32_t data_size = | 81 uint32_t data_size = |
80 static_cast<uint32_t>(params.frames_per_buffer() * params.channels() * | 82 static_cast<uint32_t>(params.frames_per_buffer() * params.channels() * |
81 writes * kBytesPerSample); | 83 writes * kBytesPerSample); |
82 // Offset Length Content | 84 // Offset Length Content |
83 // 0 4 "RIFF" | 85 // 0 4 "RIFF" |
84 EXPECT_EQ(0, strncmp(wav_header, "RIFF", 4)); | 86 EXPECT_EQ(0, strncmp(wav_header, "RIFF", 4)); |
85 // 4 4 <file length - 8> | 87 // 4 4 <file length - 8> |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 reinterpret_cast<char*>(result_interleaved.get()), | 149 reinterpret_cast<char*>(result_interleaved.get()), |
148 source_samples_ * kBytesPerSample); | 150 source_samples_ * kBytesPerSample); |
149 EXPECT_EQ(static_cast<int>(file.GetLength() - kWavHeaderSize), read); | 151 EXPECT_EQ(static_cast<int>(file.GetLength() - kWavHeaderSize), read); |
150 | 152 |
151 VerifyDataRecording(source_interleaved_.get(), result_interleaved.get(), | 153 VerifyDataRecording(source_interleaved_.get(), result_interleaved.get(), |
152 source_samples_); | 154 source_samples_); |
153 } | 155 } |
154 } | 156 } |
155 | 157 |
156 void TestDoneOnFileThread(const base::Closure& callback) { | 158 void TestDoneOnFileThread(const base::Closure& callback) { |
157 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 159 DCHECK(file_thread_.task_runner()->BelongsToCurrentThread()); |
158 | 160 |
159 callback.Run(); | 161 callback.Run(); |
160 } | 162 } |
161 | 163 |
162 void DoDebugRecording() { | 164 void DoDebugRecording() { |
163 // Write tasks are posted to BrowserThread::FILE. | 165 // Write tasks are posted to BrowserThread::FILE. |
164 for (int i = 0; i < writes_; ++i) { | 166 for (int i = 0; i < writes_; ++i) { |
165 std::unique_ptr<media::AudioBus> bus = media::AudioBus::Create( | 167 std::unique_ptr<AudioBus> bus = |
166 params_.channels(), params_.frames_per_buffer()); | 168 AudioBus::Create(params_.channels(), params_.frames_per_buffer()); |
167 | 169 |
168 bus->FromInterleaved( | 170 bus->FromInterleaved( |
169 source_interleaved_.get() + | 171 source_interleaved_.get() + |
170 i * params_.channels() * params_.frames_per_buffer(), | 172 i * params_.channels() * params_.frames_per_buffer(), |
171 params_.frames_per_buffer(), kBytesPerSample); | 173 params_.frames_per_buffer(), kBytesPerSample); |
172 | 174 |
173 input_debug_writer_->Write(std::move(bus)); | 175 debug_writer_->Write(std::move(bus)); |
174 } | 176 } |
175 } | 177 } |
176 | 178 |
177 void WaitForRecordingCompletion() { | 179 void WaitForRecordingCompletion() { |
178 media::WaitableMessageLoopEvent event; | 180 WaitableMessageLoopEvent event; |
179 | 181 |
180 // Post a task to BrowserThread::FILE indicating that all the writes are | 182 // Post a task to the file thread indicating that all the writes are done. |
181 // done. | 183 file_thread_.task_runner()->PostTask( |
182 BrowserThread::PostTask( | 184 FROM_HERE, base::Bind(&AudioDebugFileWriterTest::TestDoneOnFileThread, |
183 BrowserThread::FILE, FROM_HERE, | 185 base::Unretained(this), event.GetClosure())); |
184 base::Bind(&AudioDebugFileWriterTest::TestDoneOnFileThread, | |
185 base::Unretained(this), event.GetClosure())); | |
186 | 186 |
187 // Wait for TestDoneOnFileThread() to call event's closure. | 187 // Wait for TestDoneOnFileThread() to call event's closure. |
188 event.RunAndWait(); | 188 event.RunAndWait(); |
189 } | 189 } |
190 | 190 |
191 void RecordAndVerifyOnce() { | 191 void RecordAndVerifyOnce() { |
192 base::FilePath file_path; | 192 base::FilePath file_path; |
193 EXPECT_TRUE(base::CreateTemporaryFile(&file_path)); | 193 EXPECT_TRUE(base::CreateTemporaryFile(&file_path)); |
194 | 194 |
195 input_debug_writer_->Start(file_path); | 195 debug_writer_->Start(file_path); |
196 | 196 |
197 DoDebugRecording(); | 197 DoDebugRecording(); |
198 | 198 |
199 input_debug_writer_->Stop(); | 199 debug_writer_->Stop(); |
200 | 200 |
201 WaitForRecordingCompletion(); | 201 WaitForRecordingCompletion(); |
202 | 202 |
203 VerifyRecording(file_path); | 203 VerifyRecording(file_path); |
204 | 204 |
205 if (::testing::Test::HasFailure()) { | 205 if (::testing::Test::HasFailure()) { |
206 LOG(ERROR) << "Test failed; keeping recording(s) at [" | 206 LOG(ERROR) << "Test failed; keeping recording(s) at [" |
207 << file_path.value().c_str() << "]."; | 207 << file_path.value().c_str() << "]."; |
208 } else { | 208 } else { |
209 EXPECT_TRUE(base::DeleteFile(file_path, false)); | 209 EXPECT_TRUE(base::DeleteFile(file_path, false)); |
210 } | 210 } |
211 } | 211 } |
212 | 212 |
213 protected: | 213 protected: |
214 TestBrowserThreadBundle thread_bundle_; | 214 base::Thread file_thread_; |
| 215 base::MessageLoop message_loop_; |
215 | 216 |
216 // Writer under test. | 217 // Writer under test. |
217 std::unique_ptr<AudioDebugFileWriter> input_debug_writer_; | 218 std::unique_ptr<AudioDebugFileWriter> debug_writer_; |
218 | 219 |
219 // AudioBus parameters. | 220 // AudioBus parameters. |
220 media::AudioParameters params_; | 221 AudioParameters params_; |
221 | 222 |
222 // Number of times to write AudioBus to the file. | 223 // Number of times to write AudioBus to the file. |
223 int writes_; | 224 int writes_; |
224 | 225 |
225 // Number of samples in the source data. | 226 // Number of samples in the source data. |
226 int source_samples_; | 227 int source_samples_; |
227 | 228 |
228 // Source data. | 229 // Source data. |
229 std::unique_ptr<int16_t[]> source_interleaved_; | 230 std::unique_ptr<int16_t[]> source_interleaved_; |
230 | 231 |
231 private: | 232 private: |
232 DISALLOW_COPY_AND_ASSIGN(AudioDebugFileWriterTest); | 233 DISALLOW_COPY_AND_ASSIGN(AudioDebugFileWriterTest); |
233 }; | 234 }; |
234 | 235 |
235 class AudioDebugFileWriterBehavioralTest : public AudioDebugFileWriterTest {}; | 236 class AudioDebugFileWriterBehavioralTest : public AudioDebugFileWriterTest {}; |
236 | 237 |
237 TEST_P(AudioDebugFileWriterTest, WaveRecordingTest) { | 238 TEST_P(AudioDebugFileWriterTest, WaveRecordingTest) { |
238 input_debug_writer_.reset(new AudioDebugFileWriter(params_)); | 239 debug_writer_.reset( |
| 240 new AudioDebugFileWriter(params_, file_thread_.task_runner())); |
239 | 241 |
240 RecordAndVerifyOnce(); | 242 RecordAndVerifyOnce(); |
241 } | 243 } |
242 | 244 |
243 TEST_P(AudioDebugFileWriterBehavioralTest, | 245 TEST_P(AudioDebugFileWriterBehavioralTest, |
244 DeletedBeforeRecordingFinishedOnFileThread) { | 246 DeletedBeforeRecordingFinishedOnFileThread) { |
245 input_debug_writer_.reset(new AudioDebugFileWriter(params_)); | 247 debug_writer_.reset( |
| 248 new AudioDebugFileWriter(params_, file_thread_.task_runner())); |
246 | 249 |
247 base::FilePath file_path; | 250 base::FilePath file_path; |
248 EXPECT_TRUE(base::CreateTemporaryFile(&file_path)); | 251 EXPECT_TRUE(base::CreateTemporaryFile(&file_path)); |
249 | 252 |
250 base::WaitableEvent* wait_for_deletion = | 253 base::WaitableEvent* wait_for_deletion = |
251 new base::WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL, | 254 new base::WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL, |
252 base::WaitableEvent::InitialState::NOT_SIGNALED); | 255 base::WaitableEvent::InitialState::NOT_SIGNALED); |
253 | 256 |
254 BrowserThread::PostTask( | 257 file_thread_.task_runner()->PostTask( |
255 BrowserThread::FILE, FROM_HERE, | 258 FROM_HERE, |
256 base::Bind(&base::WaitableEvent::Wait, base::Owned(wait_for_deletion))); | 259 base::Bind(&base::WaitableEvent::Wait, base::Owned(wait_for_deletion))); |
257 | 260 |
258 input_debug_writer_->Start(file_path); | 261 debug_writer_->Start(file_path); |
259 | 262 |
260 DoDebugRecording(); | 263 DoDebugRecording(); |
261 | 264 |
262 input_debug_writer_.reset(); | 265 debug_writer_.reset(); |
263 wait_for_deletion->Signal(); | 266 wait_for_deletion->Signal(); |
264 | 267 |
265 WaitForRecordingCompletion(); | 268 WaitForRecordingCompletion(); |
266 | 269 |
267 VerifyRecording(file_path); | 270 VerifyRecording(file_path); |
268 | 271 |
269 if (::testing::Test::HasFailure()) { | 272 if (::testing::Test::HasFailure()) { |
270 LOG(ERROR) << "Test failed; keeping recording(s) at [" | 273 LOG(ERROR) << "Test failed; keeping recording(s) at [" |
271 << file_path.value().c_str() << "]."; | 274 << file_path.value().c_str() << "]."; |
272 } else { | 275 } else { |
273 EXPECT_TRUE(base::DeleteFile(file_path, false)); | 276 EXPECT_TRUE(base::DeleteFile(file_path, false)); |
274 } | 277 } |
275 } | 278 } |
276 | 279 |
277 TEST_P(AudioDebugFileWriterBehavioralTest, FileCreationError) { | 280 TEST_P(AudioDebugFileWriterBehavioralTest, FileCreationError) { |
278 input_debug_writer_.reset(new AudioDebugFileWriter(params_)); | 281 debug_writer_.reset( |
| 282 new AudioDebugFileWriter(params_, file_thread_.task_runner())); |
279 base::FilePath file_path; // Empty file name. | 283 base::FilePath file_path; // Empty file name. |
280 input_debug_writer_->Start(file_path); | 284 debug_writer_->Start(file_path); |
281 DoDebugRecording(); | 285 DoDebugRecording(); |
282 } | 286 } |
283 | 287 |
284 TEST_P(AudioDebugFileWriterBehavioralTest, StartStopStartStop) { | 288 TEST_P(AudioDebugFileWriterBehavioralTest, StartStopStartStop) { |
285 input_debug_writer_.reset(new AudioDebugFileWriter(params_)); | 289 debug_writer_.reset( |
| 290 new AudioDebugFileWriter(params_, file_thread_.task_runner())); |
286 RecordAndVerifyOnce(); | 291 RecordAndVerifyOnce(); |
287 RecordAndVerifyOnce(); | 292 RecordAndVerifyOnce(); |
288 } | 293 } |
289 | 294 |
290 TEST_P(AudioDebugFileWriterBehavioralTest, DestroyNotStarted) { | 295 TEST_P(AudioDebugFileWriterBehavioralTest, DestroyNotStarted) { |
291 input_debug_writer_.reset(new AudioDebugFileWriter(params_)); | 296 debug_writer_.reset( |
292 input_debug_writer_.reset(); | 297 new AudioDebugFileWriter(params_, file_thread_.task_runner())); |
| 298 debug_writer_.reset(); |
293 } | 299 } |
294 | 300 |
295 TEST_P(AudioDebugFileWriterBehavioralTest, DestroyStarted) { | 301 TEST_P(AudioDebugFileWriterBehavioralTest, DestroyStarted) { |
296 input_debug_writer_.reset(new AudioDebugFileWriter(params_)); | 302 debug_writer_.reset( |
| 303 new AudioDebugFileWriter(params_, file_thread_.task_runner())); |
297 base::FilePath file_path; | 304 base::FilePath file_path; |
298 EXPECT_TRUE(base::CreateTemporaryFile(&file_path)); | 305 EXPECT_TRUE(base::CreateTemporaryFile(&file_path)); |
299 input_debug_writer_->Start(file_path); | 306 debug_writer_->Start(file_path); |
300 input_debug_writer_.reset(); | 307 debug_writer_.reset(); |
301 } | 308 } |
302 | 309 |
303 INSTANTIATE_TEST_CASE_P( | 310 INSTANTIATE_TEST_CASE_P( |
304 AudioDebugFileWriterTest, | 311 AudioDebugFileWriterTest, |
305 AudioDebugFileWriterTest, | 312 AudioDebugFileWriterTest, |
306 // Using 10ms frames per buffer everywhere. | 313 // Using 10ms frames per buffer everywhere. |
307 testing::Values( | 314 testing::Values( |
308 // No writes. | 315 // No writes. |
309 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO, | 316 std::tr1::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO, |
310 44100, | 317 44100, |
311 44100 / 100, | 318 44100 / 100, |
312 0), | 319 0), |
313 // 1 write of mono. | 320 // 1 write of mono. |
314 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO, | 321 std::tr1::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO, |
315 44100, | 322 44100, |
316 44100 / 100, | 323 44100 / 100, |
317 1), | 324 1), |
318 // 1 second of mono. | 325 // 1 second of mono. |
319 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO, | 326 std::tr1::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO, |
320 44100, | 327 44100, |
321 44100 / 100, | 328 44100 / 100, |
322 100), | 329 100), |
323 // 1 second of mono, higher rate. | 330 // 1 second of mono, higher rate. |
324 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO, | 331 std::tr1::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO, |
325 48000, | 332 48000, |
326 48000 / 100, | 333 48000 / 100, |
327 100), | 334 100), |
328 // 1 second of stereo. | 335 // 1 second of stereo. |
329 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_STEREO, | 336 std::tr1::make_tuple(ChannelLayout::CHANNEL_LAYOUT_STEREO, |
330 44100, | 337 44100, |
331 44100 / 100, | 338 44100 / 100, |
332 100), | 339 100), |
333 // 15 seconds of stereo, higher rate. | 340 // 15 seconds of stereo, higher rate. |
334 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_STEREO, | 341 std::tr1::make_tuple(ChannelLayout::CHANNEL_LAYOUT_STEREO, |
335 48000, | 342 48000, |
336 48000 / 100, | 343 48000 / 100, |
337 1500))); | 344 1500))); |
338 | 345 |
339 INSTANTIATE_TEST_CASE_P( | 346 INSTANTIATE_TEST_CASE_P( |
340 AudioDebugFileWriterBehavioralTest, | 347 AudioDebugFileWriterBehavioralTest, |
341 AudioDebugFileWriterBehavioralTest, | 348 AudioDebugFileWriterBehavioralTest, |
342 // Using 10ms frames per buffer everywhere. | 349 // Using 10ms frames per buffer everywhere. |
343 testing::Values( | 350 testing::Values( |
344 // No writes. | 351 // No writes. |
345 std::tr1::make_tuple(media::ChannelLayout::CHANNEL_LAYOUT_MONO, | 352 std::tr1::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO, |
346 44100, | 353 44100, |
347 44100 / 100, | 354 44100 / 100, |
348 100))); | 355 100))); |
349 | 356 |
350 } // namespace content | 357 } // namespace media |
OLD | NEW |