| 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_enumerator.h" |
| 9 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
| 10 #include "base/files/scoped_temp_dir.h" | 10 #include "base/files/scoped_temp_dir.h" |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 #define MAYBE_WebRtcAudioQualityBrowserTest WebRtcAudioQualityBrowserTest | 47 #define MAYBE_WebRtcAudioQualityBrowserTest WebRtcAudioQualityBrowserTest |
| 48 #else | 48 #else |
| 49 // Not implemented on Android, ChromeOS etc. | 49 // Not implemented on Android, ChromeOS etc. |
| 50 #define MAYBE_WebRtcAudioQualityBrowserTest DISABLED_WebRtcAudioQualityBrowserTe
st | 50 #define MAYBE_WebRtcAudioQualityBrowserTest DISABLED_WebRtcAudioQualityBrowserTe
st |
| 51 #endif | 51 #endif |
| 52 | 52 |
| 53 } // namespace | 53 } // namespace |
| 54 | 54 |
| 55 // Test we can set up a WebRTC call and play audio through it. | 55 // Test we can set up a WebRTC call and play audio through it. |
| 56 // | 56 // |
| 57 // WARNING: this test may mess with both output and input levels on your system | |
| 58 // and may cause audio to start blasting at full volume into your | |
| 59 // headphones. | |
| 60 // | |
| 61 // If you're not a googler and want to run this test, you need to provide a | 57 // If you're not a googler and want to run this test, you need to provide a |
| 62 // pesq binary for your platform (and sox.exe on windows). Read more on how | 58 // pesq binary for your platform (and sox.exe on windows). Read more on how |
| 63 // resources are managed in chrome/test/data/webrtc/resources/README. | 59 // resources are managed in chrome/test/data/webrtc/resources/README. |
| 64 // | 60 // |
| 65 // This test will only work on machines that have been configured to record | 61 // This test will only work on machines that have been configured to record |
| 66 // their own input. | 62 // their own input. |
| 67 // | 63 // |
| 68 // On Linux: | 64 // On Linux: |
| 69 // 1. # sudo apt-get install pavucontrol sox | 65 // 1. # sudo apt-get install pavucontrol sox |
| 70 // 2. For the user who will run the test: # pavucontrol | 66 // 2. For the user who will run the test: # pavucontrol |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 // 1. Control panel > Sound > Manage audio devices. | 99 // 1. Control panel > Sound > Manage audio devices. |
| 104 // 2. In the recording tab, right-click in an empty space in the pane with the | 100 // 2. In the recording tab, right-click in an empty space in the pane with the |
| 105 // devices. Tick 'show disabled devices'. | 101 // devices. Tick 'show disabled devices'. |
| 106 // 3. You should see a 'stero mix' device - this is what your speakers output. | 102 // 3. You should see a 'stero mix' device - this is what your speakers output. |
| 107 // Right click > Properties. | 103 // Right click > Properties. |
| 108 // 4. In the Listen tab for the mix device, check the 'listen to this device' | 104 // 4. In the Listen tab for the mix device, check the 'listen to this device' |
| 109 // checkbox. Ensure the mix device is the default recording device. | 105 // checkbox. Ensure the mix device is the default recording device. |
| 110 // 5. Launch chrome and try playing a video with sound. You should see | 106 // 5. Launch chrome and try playing a video with sound. You should see |
| 111 // in the volume meter for the mix device. Configure the mix device to have | 107 // in the volume meter for the mix device. Configure the mix device to have |
| 112 // 50 / 100 in level. Also go into the playback tab, right-click Speakers, | 108 // 50 / 100 in level. Also go into the playback tab, right-click Speakers, |
| 113 // and set that level to 50 / 100. Otherwise you might get distortion in | 109 // and set that level to 50 / 100. Otherwise you will get distortion in |
| 114 // the recording. | 110 // the recording. |
| 115 class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { | 111 class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { |
| 116 public: | 112 public: |
| 117 MAYBE_WebRtcAudioQualityBrowserTest() {} | 113 MAYBE_WebRtcAudioQualityBrowserTest() {} |
| 118 void SetUpInProcessBrowserTestFixture() override { | 114 void SetUpInProcessBrowserTestFixture() override { |
| 119 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. | 115 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. |
| 120 } | 116 } |
| 121 | 117 |
| 122 void SetUpCommandLine(base::CommandLine* command_line) override { | 118 void SetUpCommandLine(base::CommandLine* command_line) override { |
| 123 EXPECT_FALSE(command_line->HasSwitch( | 119 EXPECT_FALSE(command_line->HasSwitch( |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 // Joins the recording program. Returns true on success. | 242 // Joins the recording program. Returns true on success. |
| 247 bool WaitForRecordingToEnd() { | 243 bool WaitForRecordingToEnd() { |
| 248 int exit_code = -1; | 244 int exit_code = -1; |
| 249 recording_application_.WaitForExit(&exit_code); | 245 recording_application_.WaitForExit(&exit_code); |
| 250 return exit_code == 0; | 246 return exit_code == 0; |
| 251 } | 247 } |
| 252 private: | 248 private: |
| 253 base::Process recording_application_; | 249 base::Process recording_application_; |
| 254 }; | 250 }; |
| 255 | 251 |
| 256 bool ForceMicrophoneLevelTo100Percent() { | 252 bool ForceMicrophoneVolumeTo100Percent() { |
| 257 #if defined(OS_WIN) | 253 #if defined(OS_WIN) |
| 258 // Note: the force binary isn't in tools since it's one of our own. | 254 // Note: the force binary isn't in tools since it's one of our own. |
| 259 base::CommandLine command_line(test::GetReferenceFilesDir().Append( | 255 base::CommandLine command_line(test::GetReferenceFilesDir().Append( |
| 260 FILE_PATH_LITERAL("force_mic_volume_max.exe"))); | 256 FILE_PATH_LITERAL("force_mic_volume_max.exe"))); |
| 261 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | 257 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
| 262 std::string result; | 258 std::string result; |
| 263 if (!base::GetAppOutput(command_line, &result)) { | 259 if (!base::GetAppOutput(command_line, &result)) { |
| 264 LOG(ERROR) << "Failed to set source volume: output was " << result; | 260 LOG(ERROR) << "Failed to set source volume: output was " << result; |
| 265 return false; | 261 return false; |
| 266 } | 262 } |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 484 } | 480 } |
| 485 | 481 |
| 486 // Splits |to_split| into sub-files based on silence. The file you use must have | 482 // Splits |to_split| into sub-files based on silence. The file you use must have |
| 487 // at least 500 ms periods of silence between speech segments for this to be | 483 // at least 500 ms periods of silence between speech segments for this to be |
| 488 // reliable. | 484 // reliable. |
| 489 void SplitFileOnSilenceIntoDir(const base::FilePath& to_split, | 485 void SplitFileOnSilenceIntoDir(const base::FilePath& to_split, |
| 490 const base::FilePath& workdir) { | 486 const base::FilePath& workdir) { |
| 491 // First trim beginning and end since they are tricky for the splitter. | 487 // First trim beginning and end since they are tricky for the splitter. |
| 492 base::FilePath trimmed_audio = CreateTemporaryWaveFile(); | 488 base::FilePath trimmed_audio = CreateTemporaryWaveFile(); |
| 493 | 489 |
| 494 // TODO(phoglund): find smarter way to do this and get rid of RemoveSilence. | |
| 495 ASSERT_TRUE(RemoveSilence(to_split, trimmed_audio)); | 490 ASSERT_TRUE(RemoveSilence(to_split, trimmed_audio)); |
| 496 DVLOG(0) << "Trimmed silence: " << trimmed_audio.value() << std::endl; | 491 DVLOG(0) << "Trimmed silence: " << trimmed_audio.value() << std::endl; |
| 497 | 492 |
| 498 ASSERT_TRUE(SplitFileOnSilence( | 493 ASSERT_TRUE(SplitFileOnSilence( |
| 499 trimmed_audio, workdir.Append(FILE_PATH_LITERAL("output.wav")))); | 494 trimmed_audio, workdir.Append(FILE_PATH_LITERAL("output.wav")))); |
| 500 ASSERT_TRUE(base::DeleteFile(trimmed_audio, false)); | 495 ASSERT_TRUE(base::DeleteFile(trimmed_audio, false)); |
| 501 } | 496 } |
| 502 | 497 |
| 503 // Computes the difference between the actual and reference segment. A positive | 498 // Computes the difference between the actual and reference segment. A positive |
| 504 // number x means the actual file is x dB stronger than the reference. | 499 // number x means the actual file is x dB stronger than the reference. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 541 std::string trace_name = base::StringPrintf( | 536 std::string trace_name = base::StringPrintf( |
| 542 "%s_segment_%zu", reference_file.BaseName().value().c_str(), i); | 537 "%s_segment_%zu", reference_file.BaseName().value().c_str(), i); |
| 543 perf_test::PrintResult("agc_energy_diff", perf_modifier, trace_name, | 538 perf_test::PrintResult("agc_energy_diff", perf_modifier, trace_name, |
| 544 difference_in_decibel, "dB", false); | 539 difference_in_decibel, "dB", false); |
| 545 } | 540 } |
| 546 } | 541 } |
| 547 | 542 |
| 548 void ComputeAndPrintPesqResults(const base::FilePath& reference_file, | 543 void ComputeAndPrintPesqResults(const base::FilePath& reference_file, |
| 549 const base::FilePath& recording, | 544 const base::FilePath& recording, |
| 550 const std::string& perf_modifier) { | 545 const std::string& perf_modifier) { |
| 546 base::FilePath trimmed_reference = CreateTemporaryWaveFile(); |
| 547 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); |
| 548 |
| 549 ASSERT_TRUE(RemoveSilence(reference_file, trimmed_reference)); |
| 550 ASSERT_TRUE(RemoveSilence(recording, trimmed_recording)); |
| 551 |
| 551 std::string raw_mos; | 552 std::string raw_mos; |
| 552 std::string mos_lqo; | 553 std::string mos_lqo; |
| 553 ASSERT_TRUE(RunPesq(reference_file, recording, 16000, &raw_mos, &mos_lqo)); | 554 ASSERT_TRUE(RunPesq(trimmed_reference, trimmed_recording, 16000, |
| 555 &raw_mos, &mos_lqo)); |
| 554 | 556 |
| 555 perf_test::PrintResult( | 557 perf_test::PrintResult( |
| 556 "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); | 558 "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); |
| 557 perf_test::PrintResult( | 559 perf_test::PrintResult( |
| 558 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); | 560 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); |
| 561 |
| 562 EXPECT_TRUE(base::DeleteFile(trimmed_reference, false)); |
| 563 EXPECT_TRUE(base::DeleteFile(trimmed_recording, false)); |
| 559 } | 564 } |
| 560 | 565 |
| 561 } // namespace | 566 } // namespace |
| 562 | 567 |
| 563 // Sets up a one-way WebRTC call and records its output to |recording|, using | 568 // Sets up a one-way WebRTC call and records its output to |recording|, using |
| 564 // getUserMedia. | 569 // getUserMedia. |
| 565 // | 570 // |
| 566 // |reference_file| should have at least two seconds of silence in the | 571 // |reference_file| should have at least two seconds of silence in the |
| 567 // beginning: otherwise all the reference audio will not be picked up by the | 572 // beginning: otherwise all the reference audio will not be picked up by the |
| 568 // recording. Note that the reference file will start playing as soon as the | 573 // recording. Note that the reference file will start playing as soon as the |
| 569 // audio device is up following the getUserMedia call in the left tab. The time | 574 // audio device is up following the getUserMedia call in the left tab. The time |
| 570 // it takes to negotiate a call isn't deterministic, but two seconds should be | 575 // it takes to negotiate a call isn't deterministic, but two seconds should be |
| 571 // plenty of time. Similarly, the recording time should be enough to catch the | 576 // plenty of time. Similarly, the recording time should be enough to catch the |
| 572 // whole reference file. | 577 // whole reference file. If you then silence-trim the reference file and actual |
| 578 // file, you should end up with two time-synchronized files. |
| 573 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( | 579 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( |
| 574 const base::FilePath& reference_file, | 580 const base::FilePath& reference_file, |
| 575 const base::FilePath& recording, | 581 const base::FilePath& recording, |
| 576 const std::string& constraints, | 582 const std::string& constraints, |
| 577 const base::TimeDelta recording_time) { | 583 const base::TimeDelta recording_time) { |
| 578 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); | 584 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| 579 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 585 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
| 580 ASSERT_TRUE(ForceMicrophoneLevelTo100Percent()); | 586 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); |
| 581 | 587 |
| 582 ConfigureFakeDeviceToPlayFile(reference_file); | 588 ConfigureFakeDeviceToPlayFile(reference_file); |
| 583 | 589 |
| 584 // Create a one-way call. | 590 // Create a one-way call. |
| 585 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); | 591 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); |
| 586 content::WebContents* left_tab = | 592 content::WebContents* left_tab = |
| 587 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); | 593 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
| 588 SetupPeerconnectionWithLocalStream(left_tab); | 594 SetupPeerconnectionWithLocalStream(left_tab); |
| 589 | 595 |
| 590 content::WebContents* right_tab = | 596 content::WebContents* right_tab = |
| (...skipping 27 matching lines...) Expand all Loading... |
| 618 return; | 624 return; |
| 619 } | 625 } |
| 620 | 626 |
| 621 base::FilePath reference_file = | 627 base::FilePath reference_file = |
| 622 test::GetReferenceFilesDir().Append(kReferenceFile); | 628 test::GetReferenceFilesDir().Append(kReferenceFile); |
| 623 base::FilePath recording = CreateTemporaryWaveFile(); | 629 base::FilePath recording = CreateTemporaryWaveFile(); |
| 624 | 630 |
| 625 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( | 631 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( |
| 626 reference_file, recording, kAudioOnlyCallConstraints, | 632 reference_file, recording, kAudioOnlyCallConstraints, |
| 627 base::TimeDelta::FromSeconds(25))); | 633 base::TimeDelta::FromSeconds(25))); |
| 628 ASSERT_NO_FATAL_FAILURE(ComputeAndPrintPesqResults( | 634 ComputeAndPrintPesqResults(reference_file, recording, "_getusermedia"); |
| 629 reference_file, recording, "_getusermedia")); | |
| 630 | 635 |
| 631 EXPECT_TRUE(base::DeleteFile(recording, false)); | 636 EXPECT_TRUE(base::DeleteFile(recording, false)); |
| 632 } | 637 } |
| 633 | 638 |
| 634 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 639 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
| 635 MANUAL_TestCallQualityWithAudioFromWebAudio) { | 640 MANUAL_TestCallQualityWithAudioFromWebAudio) { |
| 636 if (OnWinXp() || OnWin8()) { | 641 if (OnWinXp() || OnWin8()) { |
| 637 // http://crbug.com/379798. | 642 // http://crbug.com/379798. |
| 638 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 643 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
| 639 return; | 644 return; |
| 640 } | 645 } |
| 641 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 646 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
| 642 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); | 647 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| 643 | 648 |
| 644 ASSERT_TRUE(ForceMicrophoneLevelTo100Percent()); | 649 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); |
| 645 | 650 |
| 646 content::WebContents* left_tab = | 651 content::WebContents* left_tab = |
| 647 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 652 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
| 648 content::WebContents* right_tab = | 653 content::WebContents* right_tab = |
| 649 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 654 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
| 650 | 655 |
| 651 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); | 656 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); |
| 652 | 657 |
| 653 NegotiateCall(left_tab, right_tab); | 658 NegotiateCall(left_tab, right_tab); |
| 654 | 659 |
| 655 base::FilePath recording = CreateTemporaryWaveFile(); | 660 base::FilePath recording = CreateTemporaryWaveFile(); |
| 656 | 661 |
| 657 // Note: the sound clip is about 13 seconds: record for 20 seconds to get some | 662 // Note: the sound clip is about 13 seconds: record for 20 seconds to get some |
| 658 // safety margins on each side. | 663 // safety margins on each side. |
| 659 AudioRecorder recorder; | 664 AudioRecorder recorder; |
| 660 ASSERT_TRUE(recorder.StartRecording(base::TimeDelta::FromSeconds(20), | 665 ASSERT_TRUE(recorder.StartRecording(base::TimeDelta::FromSeconds(20), |
| 661 recording)); | 666 recording)); |
| 662 | 667 |
| 663 PlayAudioFileThroughWebAudio(left_tab); | 668 PlayAudioFileThroughWebAudio(left_tab); |
| 664 | 669 |
| 665 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); | 670 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); |
| 666 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | 671 DVLOG(0) << "Done recording to " << recording.value() << std::endl; |
| 667 | 672 |
| 668 HangUp(left_tab); | 673 HangUp(left_tab); |
| 669 | 674 |
| 670 // Compare with the reference file on disk (this is the same file we played | 675 // Compare with the reference file on disk (this is the same file we played |
| 671 // through WebAudio earlier). | 676 // through WebAudio earlier). |
| 672 base::FilePath reference_file = | 677 base::FilePath reference_file = |
| 673 test::GetReferenceFilesDir().Append(kReferenceFile); | 678 test::GetReferenceFilesDir().Append(kReferenceFile); |
| 674 ASSERT_NO_FATAL_FAILURE(ComputeAndPrintPesqResults( | 679 ComputeAndPrintPesqResults(reference_file, recording, "_webaudio"); |
| 675 reference_file, recording, "_webaudio")); | |
| 676 EXPECT_TRUE(base::DeleteFile(recording, false)); | |
| 677 } | 680 } |
| 678 | 681 |
| 679 /** | 682 /** |
| 680 * The auto gain control test plays a file into the fake microphone. Then it | 683 * The auto gain control test plays a file into the fake microphone. Then it |
| 681 * sets up a one-way WebRTC call with audio only and records Chrome's output on | 684 * sets up a one-way WebRTC call with audio only and records Chrome's output on |
| 682 * the receiving side using the audio loopback provided by the quality test | 685 * the receiving side using the audio loopback provided by the quality test |
| 683 * (see the class comments for more details). | 686 * (see the class comments for more details). |
| 684 * | 687 * |
| 685 * Then both the recording and reference file are split on silence. This creates | 688 * Then both the recording and reference file are split on silence. This creates |
| 686 * a number of segments with speech in them. The reason for this is to provide | 689 * a number of segments with speech in them. The reason for this is to provide |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 771 #endif | 774 #endif |
| 772 | 775 |
| 773 // Since the AGC is off here there should be no gain at all. | 776 // Since the AGC is off here there should be no gain at all. |
| 774 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 777 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
| 775 MAYBE_MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { | 778 MAYBE_MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { |
| 776 const char* kAudioCallWithoutAudioProcessing = | 779 const char* kAudioCallWithoutAudioProcessing = |
| 777 "{audio: { mandatory: { echoCancellation: false } } }"; | 780 "{audio: { mandatory: { echoCancellation: false } } }"; |
| 778 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | 781 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
| 779 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); | 782 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); |
| 780 } | 783 } |
| OLD | NEW |