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