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