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 |