OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 <ctime> | 5 #include <ctime> |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/files/file_enumerator.h" | |
8 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
10 #include "base/files/scoped_temp_dir.h" | |
9 #include "base/process/launch.h" | 11 #include "base/process/launch.h" |
10 #include "base/process/process.h" | 12 #include "base/process/process.h" |
11 #include "base/scoped_native_library.h" | 13 #include "base/scoped_native_library.h" |
14 #include "base/strings/string_util.h" | |
12 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
16 #include "chrome/browser/media/webrtc_browsertest_audio.h" | |
13 #include "chrome/browser/media/webrtc_browsertest_base.h" | 17 #include "chrome/browser/media/webrtc_browsertest_base.h" |
14 #include "chrome/browser/media/webrtc_browsertest_common.h" | 18 #include "chrome/browser/media/webrtc_browsertest_common.h" |
15 #include "chrome/browser/profiles/profile.h" | 19 #include "chrome/browser/profiles/profile.h" |
16 #include "chrome/browser/ui/browser.h" | 20 #include "chrome/browser/ui/browser.h" |
17 #include "chrome/browser/ui/browser_tabstrip.h" | 21 #include "chrome/browser/ui/browser_tabstrip.h" |
18 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 22 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
19 #include "chrome/common/chrome_paths.h" | 23 #include "chrome/common/chrome_paths.h" |
20 #include "chrome/common/chrome_switches.h" | 24 #include "chrome/common/chrome_switches.h" |
21 #include "chrome/test/base/ui_test_utils.h" | 25 #include "chrome/test/base/ui_test_utils.h" |
22 #include "content/public/test/browser_test_utils.h" | 26 #include "content/public/test/browser_test_utils.h" |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
142 content::WebContents* tab_contents) { | 146 content::WebContents* tab_contents) { |
143 // This calls into webaudio.js. | 147 // This calls into webaudio.js. |
144 EXPECT_EQ("ok-added", ExecuteJavascript( | 148 EXPECT_EQ("ok-added", ExecuteJavascript( |
145 "addAudioFile('" + input_file_relative_url + "')", tab_contents)); | 149 "addAudioFile('" + input_file_relative_url + "')", tab_contents)); |
146 } | 150 } |
147 | 151 |
148 void PlayAudioFileThroughWebAudio(content::WebContents* tab_contents) { | 152 void PlayAudioFileThroughWebAudio(content::WebContents* tab_contents) { |
149 EXPECT_EQ("ok-playing", ExecuteJavascript("playAudioFile()", tab_contents)); | 153 EXPECT_EQ("ok-playing", ExecuteJavascript("playAudioFile()", tab_contents)); |
150 } | 154 } |
151 | 155 |
152 base::FilePath CreateTemporaryWaveFile() { | |
153 base::FilePath filename; | |
154 EXPECT_TRUE(base::CreateTemporaryFile(&filename)); | |
155 base::FilePath wav_filename = | |
156 filename.AddExtension(FILE_PATH_LITERAL(".wav")); | |
157 EXPECT_TRUE(base::Move(filename, wav_filename)); | |
158 return wav_filename; | |
159 } | |
160 | |
161 content::WebContents* OpenPageWithoutGetUserMedia(const char* url) { | 156 content::WebContents* OpenPageWithoutGetUserMedia(const char* url) { |
162 chrome::AddTabAt(browser(), GURL(), -1, true); | 157 chrome::AddTabAt(browser(), GURL(), -1, true); |
163 ui_test_utils::NavigateToURL( | 158 ui_test_utils::NavigateToURL( |
164 browser(), embedded_test_server()->GetURL(url)); | 159 browser(), embedded_test_server()->GetURL(url)); |
165 content::WebContents* tab = | 160 content::WebContents* tab = |
166 browser()->tab_strip_model()->GetActiveWebContents(); | 161 browser()->tab_strip_model()->GetActiveWebContents(); |
167 | 162 |
168 // Prepare the peer connections manually in this test since we don't add | 163 // Prepare the peer connections manually in this test since we don't add |
169 // getUserMedia-derived media streams in this test like the other tests. | 164 // getUserMedia-derived media streams in this test like the other tests. |
170 EXPECT_EQ("ok-peerconnection-created", | 165 EXPECT_EQ("ok-peerconnection-created", |
171 ExecuteJavascript("preparePeerConnection()", tab)); | 166 ExecuteJavascript("preparePeerConnection()", tab)); |
172 return tab; | 167 return tab; |
173 } | 168 } |
169 | |
170 protected: | |
171 void TestAutoGainControl(const base::FilePath::StringType& reference_filename, | |
172 const std::string& constraints, | |
173 const std::string& perf_modifier); | |
174 }; | 174 }; |
175 | 175 |
176 namespace { | |
177 | |
176 class AudioRecorder { | 178 class AudioRecorder { |
177 public: | 179 public: |
178 AudioRecorder() {} | 180 AudioRecorder() {} |
179 ~AudioRecorder() {} | 181 ~AudioRecorder() {} |
180 | 182 |
181 // Starts the recording program for the specified duration. Returns true | 183 // Starts the recording program for the specified duration. Returns true |
182 // on success. | 184 // on success. We record in CD format unless record_cd is false, in which case |
185 // we record in DAT format. | |
186 // TODO(phoglund): make win and mac also support the record_cd parameter. Or, | |
187 // even better, make everybody use the CD format rather than DAT. | |
183 bool StartRecording(int duration_sec, const base::FilePath& output_file, | 188 bool StartRecording(int duration_sec, const base::FilePath& output_file, |
184 bool mono) { | 189 bool mono, bool record_cd) { |
185 EXPECT_FALSE(recording_application_.IsValid()) | 190 EXPECT_FALSE(recording_application_.IsValid()) |
186 << "Tried to record, but is already recording."; | 191 << "Tried to record, but is already recording."; |
187 | 192 |
188 CommandLine command_line(CommandLine::NO_PROGRAM); | 193 CommandLine command_line(CommandLine::NO_PROGRAM); |
189 #if defined(OS_WIN) | 194 #if defined(OS_WIN) |
190 // This disable is required to run SoundRecorder.exe on 64-bit Windows | 195 // This disable is required to run SoundRecorder.exe on 64-bit Windows |
191 // from a 32-bit binary. We need to load the wow64 disable function from | 196 // from a 32-bit binary. We need to load the wow64 disable function from |
192 // the DLL since it doesn't exist on Windows XP. | 197 // the DLL since it doesn't exist on Windows XP. |
193 // TODO(phoglund): find some cleaner solution than using SoundRecorder.exe. | 198 // TODO(phoglund): find some cleaner solution than using SoundRecorder.exe. |
194 base::ScopedNativeLibrary kernel32_lib(base::FilePath(L"kernel32")); | 199 base::ScopedNativeLibrary kernel32_lib(base::FilePath(L"kernel32")); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 if (mono) { | 236 if (mono) { |
232 command_line.AppendArg("remix"); | 237 command_line.AppendArg("remix"); |
233 command_line.AppendArg("-"); | 238 command_line.AppendArg("-"); |
234 } | 239 } |
235 #else | 240 #else |
236 int num_channels = mono ? 1 : 2; | 241 int num_channels = mono ? 1 : 2; |
237 command_line.SetProgram(base::FilePath("arecord")); | 242 command_line.SetProgram(base::FilePath("arecord")); |
238 command_line.AppendArg("-d"); | 243 command_line.AppendArg("-d"); |
239 command_line.AppendArg(base::StringPrintf("%d", duration_sec)); | 244 command_line.AppendArg(base::StringPrintf("%d", duration_sec)); |
240 command_line.AppendArg("-f"); | 245 command_line.AppendArg("-f"); |
241 command_line.AppendArg("dat"); | 246 if (record_cd) |
247 command_line.AppendArg("cd"); | |
248 else | |
249 command_line.AppendArg("dat"); | |
242 command_line.AppendArg("-c"); | 250 command_line.AppendArg("-c"); |
243 command_line.AppendArg(base::StringPrintf("%d", num_channels)); | 251 command_line.AppendArg(base::StringPrintf("%d", num_channels)); |
244 command_line.AppendArgPath(output_file); | 252 command_line.AppendArgPath(output_file); |
245 #endif | 253 #endif |
246 | 254 |
247 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | 255 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
248 recording_application_ = | 256 recording_application_ = |
249 base::LaunchProcess(command_line, base::LaunchOptions()); | 257 base::LaunchProcess(command_line, base::LaunchOptions()); |
250 return recording_application_.IsValid(); | 258 return recording_application_.IsValid(); |
251 } | 259 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
297 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | 305 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
298 if (!base::GetAppOutput(command_line, &result)) { | 306 if (!base::GetAppOutput(command_line, &result)) { |
299 LOG(ERROR) << "Failed to set source volume: output was " << result; | 307 LOG(ERROR) << "Failed to set source volume: output was " << result; |
300 return false; | 308 return false; |
301 } | 309 } |
302 } | 310 } |
303 #endif | 311 #endif |
304 return true; | 312 return true; |
305 } | 313 } |
306 | 314 |
315 CommandLine MakeSoxCommandLine() { | |
316 #if defined(OS_WIN) | |
317 base::FilePath sox_path = test::GetReferenceFilesDir().Append( | |
318 FILE_PATH_LITERAL("tools/sox.exe")); | |
319 if (!base::PathExists(sox_path)) { | |
320 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() | |
321 << "; you may have to provide this binary yourself."; | |
322 return false; | |
323 } | |
324 CommandLine command_line(sox_path); | |
325 #else | |
326 CommandLine command_line(base::FilePath(FILE_PATH_LITERAL("sox"))); | |
327 #endif | |
328 return command_line; | |
329 } | |
330 | |
307 // Removes silence from beginning and end of the |input_audio_file| and writes | 331 // Removes silence from beginning and end of the |input_audio_file| and writes |
308 // the result to the |output_audio_file|. Returns true on success. | 332 // the result to the |output_audio_file|. Returns true on success. |
309 bool RemoveSilence(const base::FilePath& input_file, | 333 bool RemoveSilence(const base::FilePath& input_file, |
310 const base::FilePath& output_file) { | 334 const base::FilePath& output_file) { |
311 // SOX documentation for silence command: http://sox.sourceforge.net/sox.html | 335 // SOX documentation for silence command: http://sox.sourceforge.net/sox.html |
312 // To remove the silence from both beginning and end of the audio file, we | 336 // To remove the silence from both beginning and end of the audio file, we |
313 // call sox silence command twice: once on normal file and again on its | 337 // call sox silence command twice: once on normal file and again on its |
314 // reverse, then we reverse the final output. | 338 // reverse, then we reverse the final output. |
315 // Silence parameters are (in sequence): | 339 // Silence parameters are (in sequence): |
316 // ABOVE_PERIODS: The period for which silence occurs. Value 1 is used for | 340 // ABOVE_PERIODS: The period for which silence occurs. Value 1 is used for |
317 // silence at beginning of audio. | 341 // silence at beginning of audio. |
318 // DURATION: the amount of time in seconds that non-silence must be detected | 342 // DURATION: the amount of time in seconds that non-silence must be detected |
319 // before sox stops trimming audio. | 343 // before sox stops trimming audio. |
320 // THRESHOLD: value used to indicate what sample value is treates as silence. | 344 // THRESHOLD: value used to indicate what sample value is treats as silence. |
321 const char* kAbovePeriods = "1"; | 345 const char* kAbovePeriods = "1"; |
322 const char* kDuration = "2"; | 346 const char* kDuration = "2"; |
323 const char* kTreshold = "5%"; | 347 const char* kTreshold = "3%"; |
324 | 348 |
325 #if defined(OS_WIN) | 349 CommandLine command_line = MakeSoxCommandLine(); |
326 base::FilePath sox_path = test::GetReferenceFilesDir().Append( | |
327 FILE_PATH_LITERAL("tools/sox.exe")); | |
328 if (!base::PathExists(sox_path)) { | |
329 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() | |
330 << "; you may have to provide this binary yourself."; | |
331 return false; | |
332 } | |
333 CommandLine command_line(sox_path); | |
334 #else | |
335 CommandLine command_line(base::FilePath(FILE_PATH_LITERAL("sox"))); | |
336 #endif | |
337 command_line.AppendArgPath(input_file); | 350 command_line.AppendArgPath(input_file); |
338 command_line.AppendArgPath(output_file); | 351 command_line.AppendArgPath(output_file); |
339 command_line.AppendArg("silence"); | 352 command_line.AppendArg("silence"); |
340 command_line.AppendArg(kAbovePeriods); | 353 command_line.AppendArg(kAbovePeriods); |
341 command_line.AppendArg(kDuration); | 354 command_line.AppendArg(kDuration); |
342 command_line.AppendArg(kTreshold); | 355 command_line.AppendArg(kTreshold); |
343 command_line.AppendArg("reverse"); | 356 command_line.AppendArg("reverse"); |
344 command_line.AppendArg("silence"); | 357 command_line.AppendArg("silence"); |
345 command_line.AppendArg(kAbovePeriods); | 358 command_line.AppendArg(kAbovePeriods); |
346 command_line.AppendArg(kDuration); | 359 command_line.AppendArg(kDuration); |
347 command_line.AppendArg(kTreshold); | 360 command_line.AppendArg(kTreshold); |
348 command_line.AppendArg("reverse"); | 361 command_line.AppendArg("reverse"); |
349 | 362 |
350 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | 363 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
351 std::string result; | 364 std::string result; |
352 bool ok = base::GetAppOutput(command_line, &result); | 365 bool ok = base::GetAppOutput(command_line, &result); |
353 DVLOG(0) << "Output was:\n\n" << result; | 366 DVLOG(0) << "Output was:\n\n" << result; |
354 return ok; | 367 return ok; |
355 } | 368 } |
356 | 369 |
370 // Looks for 0.3-second silences (under 1% audio power) and splits the input | |
phoglund_chromium
2014/12/17 18:52:52
I'm thinking about moving some of these new functi
| |
371 // file on those silences. Output files are written according to the output file | |
372 // template (e.g. /tmp/out.wav writes /tmp/out001.wav, /tmp/out002.wav, etc if | |
373 // there are two silence-padded regions in the file). The silences between | |
374 // speech segments must be at least 500 ms for this to be reliable. | |
375 bool SplitFileOnSilence(const base::FilePath& input_file, | |
376 const base::FilePath& output_file_template) { | |
377 CommandLine command_line = MakeSoxCommandLine(); | |
378 | |
379 // These are experimentally determined and work on the files we use. | |
380 const char* kAbovePeriods = "1"; | |
381 const char* kUnderPeriods = "1"; | |
382 const char* kDuration = "0.3"; | |
383 const char* kTreshold = "1%"; | |
384 command_line.AppendArgPath(input_file); | |
385 command_line.AppendArgPath(output_file_template); | |
386 command_line.AppendArg("silence"); | |
387 command_line.AppendArg(kAbovePeriods); | |
388 command_line.AppendArg(kDuration); | |
389 command_line.AppendArg(kTreshold); | |
390 command_line.AppendArg(kUnderPeriods); | |
391 command_line.AppendArg(kDuration); | |
392 command_line.AppendArg(kTreshold); | |
393 command_line.AppendArg(":"); | |
394 command_line.AppendArg("newfile"); | |
395 command_line.AppendArg(":"); | |
396 command_line.AppendArg("restart"); | |
397 | |
398 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | |
399 std::string result; | |
400 bool ok = base::GetAppOutput(command_line, &result); | |
401 DVLOG(0) << "Output was:\n\n" << result; | |
402 return ok; | |
403 } | |
404 | |
357 bool CanParseAsFloat(const std::string& value) { | 405 bool CanParseAsFloat(const std::string& value) { |
358 return atof(value.c_str()) != 0 || value == "0"; | 406 return atof(value.c_str()) != 0 || value == "0"; |
359 } | 407 } |
360 | 408 |
361 // Runs PESQ to compare |reference_file| to a |actual_file|. The |sample_rate| | 409 // Runs PESQ to compare |reference_file| to a |actual_file|. The |sample_rate| |
362 // can be either 16000 or 8000. | 410 // can be either 16000 or 8000. |
363 // | 411 // |
364 // PESQ is only mono-aware, so the files should preferably be recorded in mono. | 412 // PESQ is only mono-aware, so the files should preferably be recorded in mono. |
365 // Furthermore it expects the file to be 16 rather than 32 bits, even though | 413 // Furthermore it expects the file to be 16 rather than 32 bits, even though |
366 // 32 bits might work. The audio bandwidth of the two files should be the same | 414 // 32 bits might work. The audio bandwidth of the two files should be the same |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
417 // There are two tab-separated numbers on the format x.xxx, e.g. 5 chars each. | 465 // There are two tab-separated numbers on the format x.xxx, e.g. 5 chars each. |
418 std::size_t first_number_pos = anchor_pos + result_anchor.length(); | 466 std::size_t first_number_pos = anchor_pos + result_anchor.length(); |
419 *raw_mos = result.substr(first_number_pos, 5); | 467 *raw_mos = result.substr(first_number_pos, 5); |
420 EXPECT_TRUE(CanParseAsFloat(*raw_mos)) << "Failed to parse raw MOS number."; | 468 EXPECT_TRUE(CanParseAsFloat(*raw_mos)) << "Failed to parse raw MOS number."; |
421 *mos_lqo = result.substr(first_number_pos + 5 + 1, 5); | 469 *mos_lqo = result.substr(first_number_pos + 5 + 1, 5); |
422 EXPECT_TRUE(CanParseAsFloat(*mos_lqo)) << "Failed to parse MOS LQO number."; | 470 EXPECT_TRUE(CanParseAsFloat(*mos_lqo)) << "Failed to parse MOS LQO number."; |
423 | 471 |
424 return true; | 472 return true; |
425 } | 473 } |
426 | 474 |
475 base::FilePath CreateTemporaryWaveFile() { | |
476 base::FilePath filename; | |
477 EXPECT_TRUE(base::CreateTemporaryFile(&filename)); | |
478 base::FilePath wav_filename = | |
479 filename.AddExtension(FILE_PATH_LITERAL(".wav")); | |
480 EXPECT_TRUE(base::Move(filename, wav_filename)); | |
481 return wav_filename; | |
482 } | |
483 | |
484 std::vector<base::FilePath> ListWavFilesInDir(const base::FilePath& dir) { | |
485 base::FileEnumerator files(dir, false, base::FileEnumerator::FILES, | |
486 FILE_PATH_LITERAL("*.wav")); | |
487 | |
488 std::vector<base::FilePath> result; | |
489 for (base::FilePath name = files.Next(); !name.empty(); name = files.Next()) | |
490 result.push_back(name); | |
491 return result; | |
492 } | |
493 | |
494 // Splits |to_split| into sub-files base on silence. The file you use must have | |
495 // at least 500 ms periods of silence between speech segments for this to be | |
496 // reliable. | |
497 void SplitFileOnSilenceIntoDir(const base::FilePath& to_split, | |
498 const base::FilePath& workdir) { | |
499 // First trim beginning and end since they are tricky for the splitter. | |
500 base::FilePath trimmed_audio = CreateTemporaryWaveFile(); | |
501 | |
502 ASSERT_TRUE(RemoveSilence(to_split, trimmed_audio)); | |
503 DVLOG(0) << "Trimmed silence: " << trimmed_audio.value() << std::endl; | |
504 | |
505 ASSERT_TRUE(SplitFileOnSilence(trimmed_audio, workdir.Append("output.wav"))); | |
506 ASSERT_TRUE(base::DeleteFile(trimmed_audio, false)); | |
507 } | |
508 | |
509 // Computes the difference between the energy dBFS values for the actual and | |
510 // reference audio segment. Returns values as a comma-separated string. The | |
511 // segments do not have to have the exact same length, but this function will | |
512 // fail the the test if the difference is too big (if it is, we're likely | |
bjornv
2014/12/18 11:30:27
Remove one "the"
| |
513 // comparing unrelated segments with each other). | |
514 // | |
515 // The ref energy is subtracted from the actual energy, so "6" means the actual | |
516 // file is 6 dBFS units louder than the reference for one particular 10ms | |
bjornv
2014/12/18 11:30:27
dBFS here is wrong. It is simply dB. The reference
| |
517 // interval within a segment. | |
518 std::string AnalyzeOneSegment(const base::FilePath& ref_segment, | |
519 const base::FilePath& actual_segment, | |
520 int segment_number) { | |
521 std::vector<float> ref_energy = | |
522 test::ComputeAudioEnergyForWavFile(ref_segment); | |
523 std::vector<float> actual_energy = | |
524 test::ComputeAudioEnergyForWavFile(actual_segment); | |
525 | |
526 // If this fires the AGC probably enhances noise so much it goes above our | |
527 // silence threshold in the silence splitting. Either WebRTC is butchering | |
528 // audio or the silence threshold is too low. | |
529 EXPECT_LT(std::abs(static_cast<int>(ref_energy.size()) - | |
530 static_cast<int>(actual_energy.size())), 50) | |
531 << "Differing more than 50 10 ms periods for segment " << segment_number | |
532 << "; appears that output audio is very different from ref audio."; | |
533 | |
534 // Compare as many samples as possible. | |
535 size_t num_comparable_samples = | |
536 std::min(ref_energy.size(), actual_energy.size()); | |
537 | |
538 std::vector<std::string> result; | |
539 for (size_t i = 0; i < num_comparable_samples; i++) { | |
540 int rounded_value = std::round(actual_energy[i] - ref_energy[i]); | |
541 result.push_back(base::StringPrintf("%d", rounded_value)); | |
542 } | |
543 return JoinString(result, ','); | |
544 } | |
545 | |
546 void AnalyzeSegmentsAndPrintResult( | |
547 const std::vector<base::FilePath>& ref_segments, | |
548 const std::vector<base::FilePath>& actual_segments, | |
549 const base::FilePath& reference_file, | |
550 const std::string& perf_modifier) { | |
551 ASSERT_GT(ref_segments.size(), 0u) | |
552 << "Failed to split reference file on silence; sox is likely broken."; | |
553 ASSERT_EQ(ref_segments.size(), actual_segments.size()) | |
554 << "The recording did not result in the same number of audio segments " | |
555 << "after on splitting on silence; WebRTC must have deformed the audio " | |
556 << "too much."; | |
557 | |
558 for (size_t i = 0; i < ref_segments.size(); i++) { | |
bjornv
2014/12/18 11:30:27
Don't compare the first 1-2 segments, due to AGC c
| |
559 std::string dbfs_difference_csv = AnalyzeOneSegment(ref_segments[i], | |
560 actual_segments[i], | |
561 i); | |
562 std::string trace_name = base::StringPrintf( | |
563 "%s_segment_%zu", reference_file.BaseName().value().c_str(), i); | |
564 perf_test::PrintResultList("agc_energy_mod", perf_modifier, trace_name, | |
565 dbfs_difference_csv, "dBFS", false); | |
566 } | |
567 } | |
568 | |
569 } // namespace | |
570 | |
427 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 571 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
428 MANUAL_TestAudioQuality) { | 572 MANUAL_TestAudioQuality) { |
429 if (OnWinXp()) { | 573 if (OnWinXp()) { |
430 LOG(ERROR) << "This test is not implemented for Windows XP."; | 574 LOG(ERROR) << "This test is not implemented for Windows XP."; |
431 return; | 575 return; |
432 } | 576 } |
433 if (OnWin8()) { | 577 if (OnWin8()) { |
434 // http://crbug.com/379798. | 578 // http://crbug.com/379798. |
435 LOG(ERROR) << "Temporarily disabled for Win 8."; | 579 LOG(ERROR) << "Temporarily disabled for Win 8."; |
436 return; | 580 return; |
(...skipping 16 matching lines...) Expand all Loading... | |
453 // because the ready state is ok on both sides. We sleep a bit between call | 597 // because the ready state is ok on both sides. We sleep a bit between call |
454 // establishment and playing to avoid cutting off the beginning of the stream. | 598 // establishment and playing to avoid cutting off the beginning of the stream. |
455 test::SleepInJavascript(left_tab, 2000); | 599 test::SleepInJavascript(left_tab, 2000); |
456 | 600 |
457 base::FilePath recording = CreateTemporaryWaveFile(); | 601 base::FilePath recording = CreateTemporaryWaveFile(); |
458 | 602 |
459 // Note: the sound clip is about 10 seconds: record for 15 seconds to get some | 603 // Note: the sound clip is about 10 seconds: record for 15 seconds to get some |
460 // safety margins on each side. | 604 // safety margins on each side. |
461 AudioRecorder recorder; | 605 AudioRecorder recorder; |
462 static int kRecordingTimeSeconds = 15; | 606 static int kRecordingTimeSeconds = 15; |
463 ASSERT_TRUE(recorder.StartRecording(kRecordingTimeSeconds, recording, true)); | 607 ASSERT_TRUE(recorder.StartRecording(kRecordingTimeSeconds, recording, |
608 true, false)); | |
464 | 609 |
465 PlayAudioFileThroughWebAudio(left_tab); | 610 PlayAudioFileThroughWebAudio(left_tab); |
466 | 611 |
467 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); | 612 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); |
468 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | 613 DVLOG(0) << "Done recording to " << recording.value() << std::endl; |
469 | 614 |
470 HangUp(left_tab); | 615 HangUp(left_tab); |
471 | 616 |
472 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); | 617 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); |
473 | 618 |
474 ASSERT_TRUE(RemoveSilence(recording, trimmed_recording)); | 619 ASSERT_TRUE(RemoveSilence(recording, trimmed_recording)); |
475 DVLOG(0) << "Trimmed silence: " << trimmed_recording.value() << std::endl; | 620 DVLOG(0) << "Trimmed silence: " << trimmed_recording.value() << std::endl; |
476 | 621 |
477 std::string raw_mos; | 622 std::string raw_mos; |
478 std::string mos_lqo; | 623 std::string mos_lqo; |
479 base::FilePath reference_file_in_test_dir = | 624 base::FilePath reference_file_in_test_dir = |
480 test::GetReferenceFilesDir().Append(kReferenceFile); | 625 test::GetReferenceFilesDir().Append(kReferenceFile); |
481 ASSERT_TRUE(RunPesq(reference_file_in_test_dir, trimmed_recording, 16000, | 626 ASSERT_TRUE(RunPesq(reference_file_in_test_dir, trimmed_recording, 16000, |
482 &raw_mos, &mos_lqo)); | 627 &raw_mos, &mos_lqo)); |
483 | 628 |
484 perf_test::PrintResult("audio_pesq", "", "raw_mos", raw_mos, "score", true); | 629 perf_test::PrintResult("audio_pesq", "", "raw_mos", raw_mos, "score", true); |
485 perf_test::PrintResult("audio_pesq", "", "mos_lqo", mos_lqo, "score", true); | 630 perf_test::PrintResult("audio_pesq", "", "mos_lqo", mos_lqo, "score", true); |
486 | 631 |
487 EXPECT_TRUE(base::DeleteFile(recording, false)); | 632 EXPECT_TRUE(base::DeleteFile(recording, false)); |
488 EXPECT_TRUE(base::DeleteFile(trimmed_recording, false)); | 633 EXPECT_TRUE(base::DeleteFile(trimmed_recording, false)); |
489 } | 634 } |
490 | 635 |
491 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 636 /** |
492 MANUAL_TestAutoGainControlIncreasesEnergyForLowAudio) { | 637 * The auto gain control test plays a file into the fake microphone. Then it |
638 * sets up a one-way WebRTC call with audio only and records Chrome's output on | |
639 * the receiving side using the audio loopback provided by the quality test | |
640 * (see the class comments for more details). | |
641 * | |
642 * Then both the recording and reference file are split on silence. This creates | |
643 * a number of segments with speech in them. The reason for this is to provide | |
644 * a kind of synchronization mechanism so the start of each speech segment is | |
645 * compared to the start of the corresponding speech segment. This is because we | |
646 * will experience inevitable clock drift between the system clock (which runs | |
647 * the fake microphone) and the sound card (which runs play-out). Effectively | |
648 * re-synchronizing on each segment mitigates this. | |
649 * | |
650 * The silence splitting is inherently sensitive to the sound file we run on. | |
651 * Therefore the reference file must have at least 500 ms of pure silence | |
652 * between speech segments; the test will fail if the output produces more | |
653 * segments than the reference. | |
654 * | |
655 * The test reports the difference in dBFS units between the reference and | |
656 * output file per 10 ms interval in each speech segment. A value of 6 means the | |
657 * output was 6 dBFS units louder than the reference, presumably because the | |
658 * AGC applied gain to the signal. | |
659 * | |
660 * We record in CD format here (44.1 kHz) because that's what the fake input | |
661 * device currently supports, and we want to be able to compare directly. | |
662 */ | |
663 void MAYBE_WebRtcAudioQualityBrowserTest::TestAutoGainControl( | |
664 const base::FilePath::StringType& reference_filename, | |
665 const std::string& constraints, | |
666 const std::string& perf_modifier) { | |
493 if (OnWinXp()) { | 667 if (OnWinXp()) { |
494 LOG(ERROR) << "This test is not implemented for Windows XP."; | 668 LOG(ERROR) << "This test is not implemented for Windows XP."; |
495 return; | 669 return; |
496 } | 670 } |
497 if (OnWin8()) { | 671 if (OnWin8()) { |
498 // http://crbug.com/379798. | 672 // http://crbug.com/379798. |
499 LOG(ERROR) << "Temporarily disabled for Win 8."; | 673 LOG(ERROR) << "Temporarily disabled for Win 8."; |
500 return; | 674 return; |
501 } | 675 } |
502 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 676 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
503 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); | 677 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
504 | 678 |
505 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); | 679 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); |
506 | 680 |
507 ConfigureFakeDeviceToPlayFile( | 681 base::FilePath reference_file = |
508 test::GetReferenceFilesDir().Append(kAgcTestReferenceFile)); | 682 test::GetReferenceFilesDir().Append(reference_filename); |
683 ConfigureFakeDeviceToPlayFile(reference_file); | |
509 | 684 |
510 // Create a one-way call. | 685 // Create a one-way call. |
511 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); | 686 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); |
512 content::WebContents* left_tab = | 687 content::WebContents* left_tab = |
513 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, | 688 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
514 kAudioOnlyCallConstraints); | |
515 SetupPeerconnectionWithLocalStream(left_tab); | 689 SetupPeerconnectionWithLocalStream(left_tab); |
516 | 690 |
517 content::WebContents* right_tab = | 691 content::WebContents* right_tab = |
518 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 692 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
519 | 693 |
520 NegotiateCall(left_tab, right_tab); | |
521 | |
522 base::FilePath recording = CreateTemporaryWaveFile(); | 694 base::FilePath recording = CreateTemporaryWaveFile(); |
523 | 695 |
524 AudioRecorder recorder; | 696 AudioRecorder recorder; |
525 static int kRecordingTimeSeconds = 10; | 697 static int kRecordingTimeSeconds = 25; |
526 ASSERT_TRUE(recorder.StartRecording(kRecordingTimeSeconds, recording, true)); | 698 ASSERT_TRUE(recorder.StartRecording(kRecordingTimeSeconds, recording, false, |
699 true)); | |
700 | |
701 NegotiateCall(left_tab, right_tab); | |
527 | 702 |
528 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); | 703 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); |
529 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | 704 DVLOG(0) << "Done recording to " << recording.value() << std::endl; |
530 | 705 |
531 HangUp(left_tab); | 706 HangUp(left_tab); |
532 | 707 |
533 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); | 708 // Call Take() on the scoped temp dirs if you want to look at the files after |
709 // the test exits (the default is to delete the files). | |
710 base::ScopedTempDir split_ref_files; | |
711 ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); | |
712 ASSERT_NO_FATAL_FAILURE( | |
713 SplitFileOnSilenceIntoDir(reference_file, split_ref_files.path())); | |
714 std::vector<base::FilePath> ref_segments = | |
715 ListWavFilesInDir(split_ref_files.path()); | |
534 | 716 |
535 ASSERT_TRUE(RemoveSilence(recording, trimmed_recording)); | 717 base::ScopedTempDir split_actual_files; |
536 DVLOG(0) << "Trimmed silence: " << trimmed_recording.value() << std::endl; | 718 ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); |
719 ASSERT_NO_FATAL_FAILURE( | |
720 SplitFileOnSilenceIntoDir(recording, split_actual_files.path())); | |
721 std::vector<base::FilePath> actual_segments = | |
722 ListWavFilesInDir(split_actual_files.path()); | |
537 | 723 |
538 // TODO(phoglund): invoke bjornv's audio energy analysis tool on the trimmed | 724 AnalyzeSegmentsAndPrintResult(ref_segments, actual_segments, reference_file, |
539 // recording and log the result. | 725 perf_modifier); |
540 | 726 |
541 EXPECT_TRUE(base::DeleteFile(recording, false)); | 727 EXPECT_TRUE(base::DeleteFile(recording, false)); |
542 EXPECT_TRUE(base::DeleteFile(trimmed_recording, false)); | |
543 } | 728 } |
729 | |
730 // Only implemented for Linux for now. | |
731 #if defined(OS_LINUX) | |
732 #define MAYBE_MANUAL_TestAutoGainControlOnLowAudio \ | |
733 MANUAL_TestAutoGainControlOnLowAudio | |
734 #else | |
735 #define MAYBE_MANUAL_TestAutoGainControlOnLowAudio \ | |
736 DISABLED_MANUAL_TestAutoGainControlOnLowAudio | |
737 #endif | |
738 | |
739 // The AGC should apply gain here on the order of 5-6 dBFS units. | |
740 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | |
741 MAYBE_MANUAL_TestAutoGainControlOnLowAudio) { | |
742 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | |
743 kAgcTestReferenceFile, kAudioOnlyCallConstraints, "with_agc")); | |
744 } | |
745 | |
746 // Only implemented for Linux for now. | |
747 #if defined(OS_LINUX) | |
748 #define MAYBE_MANUAL_TestComputeGainWithAudioProcessingOff \ | |
749 MANUAL_TestComputeGainWithAudioProcessingOff | |
750 #else | |
751 #define MAYBE_MANUAL_TestComputeGainWithAudioProcessingOff \ | |
752 DISABLED_MANUAL_TestComputeGainWithAudioProcessingOff | |
753 #endif | |
754 | |
755 // Since the AGC is off here there should be no gain at all. | |
756 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | |
757 MAYBE_MANUAL_TestComputeGainWithAudioProcessingOff) { | |
758 const char* kAudioCallWithoutAudioProcessing = | |
759 "{audio: { mandatory: { echoCancellation: false } } }"; | |
760 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | |
761 kAgcTestReferenceFile, kAudioCallWithoutAudioProcessing, "no_agc")); | |
762 } | |
763 | |
OLD | NEW |