Index: chrome/browser/media/webrtc_audio_quality_browsertest.cc |
diff --git a/chrome/browser/media/webrtc_audio_quality_browsertest.cc b/chrome/browser/media/webrtc_audio_quality_browsertest.cc |
deleted file mode 100644 |
index a12e15fbda8389c02eb39d8bfe8b7bbdd0d5910d..0000000000000000000000000000000000000000 |
--- a/chrome/browser/media/webrtc_audio_quality_browsertest.cc |
+++ /dev/null |
@@ -1,823 +0,0 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include <stddef.h> |
- |
-#include <ctime> |
- |
-#include "base/command_line.h" |
-#include "base/files/file_enumerator.h" |
-#include "base/files/file_util.h" |
-#include "base/files/scoped_temp_dir.h" |
-#include "base/macros.h" |
-#include "base/process/launch.h" |
-#include "base/process/process.h" |
-#include "base/scoped_native_library.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "build/build_config.h" |
-#include "chrome/browser/media/webrtc_browsertest_audio.h" |
-#include "chrome/browser/media/webrtc_browsertest_base.h" |
-#include "chrome/browser/media/webrtc_browsertest_common.h" |
-#include "chrome/browser/profiles/profile.h" |
-#include "chrome/browser/ui/browser.h" |
-#include "chrome/browser/ui/browser_tabstrip.h" |
-#include "chrome/browser/ui/tabs/tab_strip_model.h" |
-#include "chrome/common/chrome_paths.h" |
-#include "chrome/common/chrome_switches.h" |
-#include "chrome/test/base/ui_test_utils.h" |
-#include "content/public/common/content_switches.h" |
-#include "content/public/test/browser_test_utils.h" |
-#include "media/base/audio_parameters.h" |
-#include "media/base/media_switches.h" |
-#include "net/test/embedded_test_server/embedded_test_server.h" |
-#include "testing/perf/perf_test.h" |
- |
-namespace { |
- |
-static const base::FilePath::CharType kReferenceFile[] = |
- FILE_PATH_LITERAL("speech_44kHz_16bit_stereo.wav"); |
- |
-// The javascript will load the reference file relative to its location, |
-// which is in /webrtc on the web server. The files we are looking for are in |
-// webrtc/resources in the chrome/test/data folder. |
-static const char kReferenceFileRelativeUrl[] = |
- "resources/speech_44kHz_16bit_stereo.wav"; |
- |
-static const char kWebRtcAudioTestHtmlPage[] = |
- "/webrtc/webrtc_audio_quality_test.html"; |
- |
-// For the AGC test, there are 6 speech segments split on silence. If one |
-// segment is significantly different in length compared to the same segment in |
-// the reference file, there's something fishy going on. |
-const int kMaxAgcSegmentDiffMs = |
-#if defined(OS_MACOSX) |
- // Something is different on Mac; http://crbug.com/477653. |
- 600; |
-#else |
- 200; |
-#endif |
- |
-#if defined(OS_LINUX) || defined(OS_MACOSX) |
-#define MAYBE_WebRtcAudioQualityBrowserTest WebRtcAudioQualityBrowserTest |
-#else |
-// Not implemented on Android, ChromeOS etc. |
-// Currently fails on Windows bots. http://crbug.com/642294. |
-#define MAYBE_WebRtcAudioQualityBrowserTest DISABLED_WebRtcAudioQualityBrowserTest |
-#endif |
- |
-} // namespace |
- |
-// Test we can set up a WebRTC call and play audio through it. |
-// |
-// If you're not a googler and want to run this test, you need to provide a |
-// pesq binary for your platform (and sox.exe on windows). Read more on how |
-// resources are managed in chrome/test/data/webrtc/resources/README. |
-// |
-// This test will only work on machines that have been configured to record |
-// their own input. |
-// |
-// On Linux: |
-// 1. # sudo apt-get install pavucontrol sox |
-// 2. For the user who will run the test: # pavucontrol |
-// 3. In a separate terminal, # arecord dummy |
-// 4. In pavucontrol, go to the recording tab. |
-// 5. For the ALSA plugin [aplay]: ALSA Capture from, change from <x> to |
-// <Monitor of x>, where x is whatever your primary sound device is called. |
-// 6. Try launching chrome as the target user on the target machine, try |
-// playing, say, a YouTube video, and record with # arecord -f dat tmp.dat. |
-// Verify the recording with aplay (should have recorded what you played |
-// from chrome). |
-// |
-// Note: the volume for ALL your input devices will be forced to 100% by |
-// running this test on Linux. |
-// |
-// On Mac: |
-// TODO(phoglund): download sox from gs instead. |
-// 1. Get SoundFlower: http://rogueamoeba.com/freebies/soundflower/download.php |
-// 2. Install it + reboot. |
-// 3. Install MacPorts (http://www.macports.org/). |
-// 4. Install sox: sudo port install sox. |
-// 5. (For Chrome bots) Ensure sox and rec are reachable from the env the test |
-// executes in (sox and rec tends to install in /opt/, which generally isn't |
-// in the Chrome bots' env). For instance, run |
-// sudo ln -s /opt/local/bin/rec /usr/local/bin/rec |
-// sudo ln -s /opt/local/bin/sox /usr/local/bin/sox |
-// 6. In Sound Preferences, set both input and output to Soundflower (2ch). |
-// Note: You will no longer hear audio on this machine, and it will no |
-// longer use any built-in mics. |
-// 7. Try launching chrome as the target user on the target machine, try |
-// playing, say, a YouTube video, and record with 'rec test.wav trim 0 5'. |
-// Stop the video in chrome and try playing back the file; you should hear |
-// a recording of the video (note; if you play back on the target machine |
-// you must revert the changes in step 3 first). |
-// |
-// On Windows 7: |
-// 1. Control panel > Sound > Manage audio devices. |
-// 2. In the recording tab, right-click in an empty space in the pane with the |
-// devices. Tick 'show disabled devices'. |
-// 3. You should see a 'stero mix' device - this is what your speakers output. |
-// Right click > Properties. |
-// 4. In the Listen tab for the mix device, check the 'listen to this device' |
-// checkbox. Ensure the mix device is the default recording device. |
-// 5. Launch chrome and try playing a video with sound. You should see |
-// in the volume meter for the mix device. Configure the mix device to have |
-// 50 / 100 in level. Also go into the playback tab, right-click Speakers, |
-// and set that level to 50 / 100. Otherwise you will get distortion in |
-// the recording. |
-class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { |
- public: |
- MAYBE_WebRtcAudioQualityBrowserTest() {} |
- void SetUpInProcessBrowserTestFixture() override { |
- DetectErrorsInJavaScript(); // Look for errors in our rather complex js. |
- } |
- |
- void SetUpCommandLine(base::CommandLine* command_line) override { |
- EXPECT_FALSE(command_line->HasSwitch( |
- switches::kUseFakeUIForMediaStream)); |
- |
- // The WebAudio-based tests don't care what devices are available to |
- // getUserMedia, and the getUserMedia-based tests will play back a file |
- // through the fake device using using --use-file-for-fake-audio-capture. |
- command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream); |
- |
- // Add loopback interface such that there is always connectivity. |
- command_line->AppendSwitch(switches::kAllowLoopbackInPeerConnection); |
- } |
- |
- void ConfigureFakeDeviceToPlayFile(const base::FilePath& wav_file_path) { |
- base::CommandLine::ForCurrentProcess()->AppendSwitchNative( |
- switches::kUseFileForFakeAudioCapture, |
- wav_file_path.value() + FILE_PATH_LITERAL("%noloop")); |
- } |
- |
- void AddAudioFileToWebAudio(const std::string& input_file_relative_url, |
- content::WebContents* tab_contents) { |
- // This calls into webaudio.js. |
- EXPECT_EQ("ok-added", ExecuteJavascript( |
- "addAudioFile('" + input_file_relative_url + "')", tab_contents)); |
- } |
- |
- void PlayAudioFileThroughWebAudio(content::WebContents* tab_contents) { |
- EXPECT_EQ("ok-playing", ExecuteJavascript("playAudioFile()", tab_contents)); |
- } |
- |
- content::WebContents* OpenPageWithoutGetUserMedia(const char* url) { |
- chrome::AddTabAt(browser(), GURL(), -1, true); |
- ui_test_utils::NavigateToURL( |
- browser(), embedded_test_server()->GetURL(url)); |
- content::WebContents* tab = |
- browser()->tab_strip_model()->GetActiveWebContents(); |
- |
- // Prepare the peer connections manually in this test since we don't add |
- // getUserMedia-derived media streams in this test like the other tests. |
- EXPECT_EQ("ok-peerconnection-created", |
- ExecuteJavascript("preparePeerConnection()", tab)); |
- return tab; |
- } |
- |
- void MuteMediaElement(const std::string& element_id, |
- content::WebContents* tab_contents) { |
- EXPECT_EQ("ok-muted", ExecuteJavascript( |
- "setMediaElementMuted('" + element_id + "', true)", tab_contents)); |
- } |
- |
- protected: |
- void TestAutoGainControl(const base::FilePath::StringType& reference_filename, |
- const std::string& constraints, |
- const std::string& perf_modifier); |
- void SetupAndRecordAudioCall(const base::FilePath& reference_file, |
- const base::FilePath& recording, |
- const std::string& constraints, |
- const base::TimeDelta recording_time); |
- void TestWithFakeDeviceGetUserMedia(const std::string& constraints, |
- const std::string& perf_modifier); |
-}; |
- |
-namespace { |
- |
-class AudioRecorder { |
- public: |
- AudioRecorder() {} |
- ~AudioRecorder() {} |
- |
- // Starts the recording program for the specified duration. Returns true |
- // on success. We record in 16-bit 44.1 kHz Stereo (mostly because that's |
- // what SoundRecorder.exe will give us and we can't change that). |
- bool StartRecording(base::TimeDelta recording_time, |
- const base::FilePath& output_file) { |
- EXPECT_FALSE(recording_application_.IsValid()) |
- << "Tried to record, but is already recording."; |
- |
- int duration_sec = static_cast<int>(recording_time.InSeconds()); |
- base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
- |
-#if defined(OS_WIN) |
- // This disable is required to run SoundRecorder.exe on 64-bit Windows |
- // from a 32-bit binary. We need to load the wow64 disable function from |
- // the DLL since it doesn't exist on Windows XP. |
- base::ScopedNativeLibrary kernel32_lib(base::FilePath(L"kernel32")); |
- if (kernel32_lib.is_valid()) { |
- typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*); |
- Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection; |
- wow_64_disable_wow_64_fs_redirection = |
- reinterpret_cast<Wow64DisableWow64FSRedirection>( |
- kernel32_lib.GetFunctionPointer( |
- "Wow64DisableWow64FsRedirection")); |
- if (wow_64_disable_wow_64_fs_redirection != NULL) { |
- PVOID* ignored = NULL; |
- wow_64_disable_wow_64_fs_redirection(ignored); |
- } |
- } |
- |
- char duration_in_hms[128] = {0}; |
- struct tm duration_tm = {0}; |
- duration_tm.tm_sec = duration_sec; |
- EXPECT_NE(0u, strftime(duration_in_hms, arraysize(duration_in_hms), |
- "%H:%M:%S", &duration_tm)); |
- |
- command_line.SetProgram( |
- base::FilePath(FILE_PATH_LITERAL("SoundRecorder.exe"))); |
- command_line.AppendArg("/FILE"); |
- command_line.AppendArgPath(output_file); |
- command_line.AppendArg("/DURATION"); |
- command_line.AppendArg(duration_in_hms); |
-#elif defined(OS_MACOSX) |
- command_line.SetProgram(base::FilePath("rec")); |
- command_line.AppendArg("-b"); |
- command_line.AppendArg("16"); |
- command_line.AppendArg("-q"); |
- command_line.AppendArgPath(output_file); |
- command_line.AppendArg("trim"); |
- command_line.AppendArg("0"); |
- command_line.AppendArg(base::IntToString(duration_sec)); |
-#else |
- command_line.SetProgram(base::FilePath("arecord")); |
- command_line.AppendArg("-d"); |
- command_line.AppendArg(base::IntToString(duration_sec)); |
- command_line.AppendArg("-f"); |
- command_line.AppendArg("cd"); |
- command_line.AppendArg("-c"); |
- command_line.AppendArg("2"); |
- command_line.AppendArgPath(output_file); |
-#endif |
- |
- DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
- recording_application_ = |
- base::LaunchProcess(command_line, base::LaunchOptions()); |
- return recording_application_.IsValid(); |
- } |
- |
- // Joins the recording program. Returns true on success. |
- bool WaitForRecordingToEnd() { |
- int exit_code = -1; |
- recording_application_.WaitForExit(&exit_code); |
- return exit_code == 0; |
- } |
- private: |
- base::Process recording_application_; |
-}; |
- |
-bool ForceMicrophoneVolumeTo100Percent() { |
-#if defined(OS_WIN) |
- // Note: the force binary isn't in tools since it's one of our own. |
- base::CommandLine command_line(test::GetReferenceFilesDir().Append( |
- FILE_PATH_LITERAL("force_mic_volume_max.exe"))); |
- DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
- std::string result; |
- if (!base::GetAppOutput(command_line, &result)) { |
- LOG(ERROR) << "Failed to set source volume: output was " << result; |
- return false; |
- } |
-#elif defined(OS_MACOSX) |
- base::CommandLine command_line( |
- base::FilePath(FILE_PATH_LITERAL("osascript"))); |
- command_line.AppendArg("-e"); |
- command_line.AppendArg("set volume input volume 100"); |
- command_line.AppendArg("-e"); |
- command_line.AppendArg("set volume output volume 85"); |
- |
- std::string result; |
- if (!base::GetAppOutput(command_line, &result)) { |
- LOG(ERROR) << "Failed to set source volume: output was " << result; |
- return false; |
- } |
-#else |
- // Just force the volume of, say the first 5 devices. A machine will rarely |
- // have more input sources than that. This is way easier than finding the |
- // input device we happen to be using. |
- for (int device_index = 0; device_index < 5; ++device_index) { |
- std::string result; |
- const std::string kHundredPercentVolume = "65536"; |
- base::CommandLine command_line(base::FilePath(FILE_PATH_LITERAL("pacmd"))); |
- command_line.AppendArg("set-source-volume"); |
- command_line.AppendArg(base::IntToString(device_index)); |
- command_line.AppendArg(kHundredPercentVolume); |
- DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
- if (!base::GetAppOutput(command_line, &result)) { |
- LOG(ERROR) << "Failed to set source volume: output was " << result; |
- return false; |
- } |
- } |
-#endif |
- return true; |
-} |
- |
-// Sox is the "Swiss army knife" of audio processing. We mainly use it for |
-// silence trimming. See http://sox.sourceforge.net. |
-base::CommandLine MakeSoxCommandLine() { |
-#if defined(OS_WIN) |
- base::FilePath sox_path = test::GetToolForPlatform("sox"); |
- if (!base::PathExists(sox_path)) { |
- LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() |
- << "; you may have to provide this binary yourself."; |
- return base::CommandLine(base::CommandLine::NO_PROGRAM); |
- } |
- base::CommandLine command_line(sox_path); |
-#else |
- // TODO(phoglund): call checked-in sox rather than system sox on mac/linux. |
- // Same for rec invocations on Mac, above. |
- base::CommandLine command_line(base::FilePath(FILE_PATH_LITERAL("sox"))); |
-#endif |
- return command_line; |
-} |
- |
-// Removes silence from beginning and end of the |input_audio_file| and writes |
-// the result to the |output_audio_file|. Returns true on success. |
-bool RemoveSilence(const base::FilePath& input_file, |
- const base::FilePath& output_file) { |
- // SOX documentation for silence command: http://sox.sourceforge.net/sox.html |
- // To remove the silence from both beginning and end of the audio file, we |
- // call sox silence command twice: once on normal file and again on its |
- // reverse, then we reverse the final output. |
- // Silence parameters are (in sequence): |
- // ABOVE_PERIODS: The period for which silence occurs. Value 1 is used for |
- // silence at beginning of audio. |
- // DURATION: the amount of time in seconds that non-silence must be detected |
- // before sox stops trimming audio. |
- // THRESHOLD: value used to indicate what sample value is treats as silence. |
- const char* kAbovePeriods = "1"; |
- const char* kDuration = "2"; |
- const char* kTreshold = "1.5%"; |
- |
- base::CommandLine command_line = MakeSoxCommandLine(); |
- if (command_line.GetProgram().empty()) |
- return false; |
- command_line.AppendArgPath(input_file); |
- command_line.AppendArgPath(output_file); |
- command_line.AppendArg("silence"); |
- command_line.AppendArg(kAbovePeriods); |
- command_line.AppendArg(kDuration); |
- command_line.AppendArg(kTreshold); |
- command_line.AppendArg("reverse"); |
- command_line.AppendArg("silence"); |
- command_line.AppendArg(kAbovePeriods); |
- command_line.AppendArg(kDuration); |
- command_line.AppendArg(kTreshold); |
- command_line.AppendArg("reverse"); |
- |
- DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
- std::string result; |
- bool ok = base::GetAppOutput(command_line, &result); |
- DVLOG(0) << "Output was:\n\n" << result; |
- return ok; |
-} |
- |
-// Looks for 0.2 second audio segments surrounded by silences under 0.3% audio |
-// power and splits the input file on those silences. Output files are written |
-// according to the output file template (e.g. /tmp/out.wav writes |
-// /tmp/out001.wav, /tmp/out002.wav, etc if there are two silence-padded |
-// regions in the file). The silences between speech segments must be at |
-// least 500 ms for this to be reliable. |
-bool SplitFileOnSilence(const base::FilePath& input_file, |
- const base::FilePath& output_file_template) { |
- base::CommandLine command_line = MakeSoxCommandLine(); |
- if (command_line.GetProgram().empty()) |
- return false; |
- |
- // These are experimentally determined and work on the files we use. |
- const char* kAbovePeriods = "1"; |
- const char* kUnderPeriods = "1"; |
- const char* kDuration = "0.2"; |
- const char* kTreshold = "0.5%"; |
- command_line.AppendArgPath(input_file); |
- command_line.AppendArgPath(output_file_template); |
- command_line.AppendArg("silence"); |
- command_line.AppendArg(kAbovePeriods); |
- command_line.AppendArg(kDuration); |
- command_line.AppendArg(kTreshold); |
- command_line.AppendArg(kUnderPeriods); |
- command_line.AppendArg(kDuration); |
- command_line.AppendArg(kTreshold); |
- command_line.AppendArg(":"); |
- command_line.AppendArg("newfile"); |
- command_line.AppendArg(":"); |
- command_line.AppendArg("restart"); |
- |
- DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
- std::string result; |
- bool ok = base::GetAppOutput(command_line, &result); |
- DVLOG(0) << "Output was:\n\n" << result; |
- return ok; |
-} |
- |
-bool CanParseAsFloat(const std::string& value) { |
- return atof(value.c_str()) != 0 || value == "0"; |
-} |
- |
-// Runs PESQ to compare |reference_file| to a |actual_file|. The |sample_rate| |
-// can be either 16000 or 8000. |
-// |
-// PESQ is only mono-aware, so the files should preferably be recorded in mono. |
-// Furthermore it expects the file to be 16 rather than 32 bits, even though |
-// 32 bits might work. The audio bandwidth of the two files should be the same |
-// e.g. don't compare a 32 kHz file to a 8 kHz file. |
-// |
-// The raw score in MOS is written to |raw_mos|, whereas the MOS-LQO score is |
-// written to mos_lqo. The scores are returned as floats in string form (e.g. |
-// "3.145", etc). Returns true on success. |
-bool RunPesq(const base::FilePath& reference_file, |
- const base::FilePath& actual_file, |
- int sample_rate, std::string* raw_mos, std::string* mos_lqo) { |
- // PESQ will break if the paths are too long (!). |
- EXPECT_LT(reference_file.value().length(), 128u); |
- EXPECT_LT(actual_file.value().length(), 128u); |
- |
- base::FilePath pesq_path = test::GetToolForPlatform("pesq"); |
- if (!base::PathExists(pesq_path)) { |
- LOG(ERROR) << "Missing PESQ binary in " << pesq_path.value() |
- << "; you may have to provide this binary yourself."; |
- return false; |
- } |
- |
- base::CommandLine command_line(pesq_path); |
- command_line.AppendArg(base::StringPrintf("+%d", sample_rate)); |
- command_line.AppendArgPath(reference_file); |
- command_line.AppendArgPath(actual_file); |
- |
- DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
- std::string result; |
- if (!base::GetAppOutput(command_line, &result)) { |
- LOG(ERROR) << "Failed to run PESQ."; |
- return false; |
- } |
- DVLOG(0) << "Output was:\n\n" << result; |
- |
- const std::string result_anchor = "Prediction (Raw MOS, MOS-LQO): = "; |
- std::size_t anchor_pos = result.find(result_anchor); |
- if (anchor_pos == std::string::npos) { |
- LOG(ERROR) << "PESQ was not able to compute a score; we probably recorded " |
- << "only silence. Please check the output/input volume levels."; |
- return false; |
- } |
- |
- // There are two tab-separated numbers on the format x.xxx, e.g. 5 chars each. |
- std::size_t first_number_pos = anchor_pos + result_anchor.length(); |
- *raw_mos = result.substr(first_number_pos, 5); |
- EXPECT_TRUE(CanParseAsFloat(*raw_mos)) << "Failed to parse raw MOS number."; |
- *mos_lqo = result.substr(first_number_pos + 5 + 1, 5); |
- EXPECT_TRUE(CanParseAsFloat(*mos_lqo)) << "Failed to parse MOS LQO number."; |
- |
- return true; |
-} |
- |
-base::FilePath CreateTemporaryWaveFile() { |
- base::FilePath filename; |
- EXPECT_TRUE(base::CreateTemporaryFile(&filename)); |
- base::FilePath wav_filename = |
- filename.AddExtension(FILE_PATH_LITERAL(".wav")); |
- EXPECT_TRUE(base::Move(filename, wav_filename)); |
- return wav_filename; |
-} |
- |
-void DeleteFileUnlessTestFailed(const base::FilePath& path, bool recursive) { |
- if (::testing::Test::HasFailure()) |
- printf("Test failed; keeping recording(s) at\n\t%" PRFilePath ".\n", |
- path.value().c_str()); |
- else |
- EXPECT_TRUE(base::DeleteFile(path, recursive)); |
-} |
- |
-std::vector<base::FilePath> ListWavFilesInDir(const base::FilePath& dir) { |
- base::FileEnumerator files(dir, false, base::FileEnumerator::FILES, |
- FILE_PATH_LITERAL("*.wav")); |
- |
- std::vector<base::FilePath> result; |
- for (base::FilePath name = files.Next(); !name.empty(); name = files.Next()) |
- result.push_back(name); |
- return result; |
-} |
- |
-// Splits |to_split| into sub-files based on silence. The file you use must have |
-// at least 500 ms periods of silence between speech segments for this to be |
-// reliable. |
-void SplitFileOnSilenceIntoDir(const base::FilePath& to_split, |
- const base::FilePath& workdir) { |
- // First trim beginning and end since they are tricky for the splitter. |
- base::FilePath trimmed_audio = CreateTemporaryWaveFile(); |
- |
- ASSERT_TRUE(RemoveSilence(to_split, trimmed_audio)); |
- DVLOG(0) << "Trimmed silence: " << trimmed_audio.value() << std::endl; |
- |
- ASSERT_TRUE(SplitFileOnSilence( |
- trimmed_audio, workdir.Append(FILE_PATH_LITERAL("output.wav")))); |
- DeleteFileUnlessTestFailed(trimmed_audio, false); |
-} |
- |
-// Computes the difference between the actual and reference segment. A positive |
-// number x means the actual file is x dB stronger than the reference. |
-float AnalyzeOneSegment(const base::FilePath& ref_segment, |
- const base::FilePath& actual_segment, |
- int segment_number) { |
- media::AudioParameters ref_parameters; |
- media::AudioParameters actual_parameters; |
- float ref_energy = |
- test::ComputeAudioEnergyForWavFile(ref_segment, &ref_parameters); |
- float actual_energy = |
- test::ComputeAudioEnergyForWavFile(actual_segment, &actual_parameters); |
- |
- base::TimeDelta difference_in_length = ref_parameters.GetBufferDuration() - |
- actual_parameters.GetBufferDuration(); |
- |
- EXPECT_LE(difference_in_length, |
- base::TimeDelta::FromMilliseconds(kMaxAgcSegmentDiffMs)) |
- << "Segments differ " << difference_in_length.InMilliseconds() << " ms " |
- << "in length for segment " << segment_number << "; we're likely " |
- << "comparing unrelated segments or silence splitting is busted."; |
- |
- return actual_energy - ref_energy; |
-} |
- |
-std::string MakeTraceName(const base::FilePath& ref_filename, |
- size_t segment_number) { |
- std::string ascii_filename; |
-#if defined(OS_WIN) |
- ascii_filename = base::WideToUTF8(ref_filename.BaseName().value()); |
-#else |
- ascii_filename = ref_filename.BaseName().value(); |
-#endif |
- return base::StringPrintf( |
- "%s_segment_%d", ascii_filename.c_str(), (int)segment_number); |
-} |
- |
-void AnalyzeSegmentsAndPrintResult( |
- const std::vector<base::FilePath>& ref_segments, |
- const std::vector<base::FilePath>& actual_segments, |
- const base::FilePath& reference_file, |
- const std::string& perf_modifier) { |
- ASSERT_GT(ref_segments.size(), 0u) |
- << "Failed to split reference file on silence; sox is likely broken."; |
- ASSERT_EQ(ref_segments.size(), actual_segments.size()) |
- << "The recording did not result in the same number of audio segments " |
- << "after on splitting on silence; WebRTC must have deformed the audio " |
- << "too much."; |
- |
- for (size_t i = 0; i < ref_segments.size(); i++) { |
- float difference_in_decibel = AnalyzeOneSegment(ref_segments[i], |
- actual_segments[i], |
- i); |
- std::string trace_name = MakeTraceName(reference_file, i); |
- perf_test::PrintResult("agc_energy_diff", perf_modifier, trace_name, |
- difference_in_decibel, "dB", false); |
- } |
-} |
- |
-void ComputeAndPrintPesqResults(const base::FilePath& reference_file, |
- const base::FilePath& recording, |
- const std::string& perf_modifier) { |
- base::FilePath trimmed_reference = CreateTemporaryWaveFile(); |
- base::FilePath trimmed_recording = CreateTemporaryWaveFile(); |
- |
- ASSERT_TRUE(RemoveSilence(reference_file, trimmed_reference)); |
- ASSERT_TRUE(RemoveSilence(recording, trimmed_recording)); |
- |
- std::string raw_mos; |
- std::string mos_lqo; |
- bool succeeded = RunPesq(trimmed_reference, trimmed_recording, 16000, |
- &raw_mos, &mos_lqo); |
- EXPECT_TRUE(succeeded) << "Failed to run PESQ."; |
- if (succeeded) { |
- perf_test::PrintResult( |
- "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); |
- perf_test::PrintResult( |
- "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); |
- } |
- |
- DeleteFileUnlessTestFailed(trimmed_reference, false); |
- DeleteFileUnlessTestFailed(trimmed_recording, false); |
-} |
- |
-} // namespace |
- |
-// Sets up a two-way WebRTC call and records its output to |recording|, using |
-// getUserMedia. |
-// |
-// |reference_file| should have at least five seconds of silence in the |
-// beginning: otherwise all the reference audio will not be picked up by the |
-// recording. Note that the reference file will start playing as soon as the |
-// audio device is up following the getUserMedia call in the left tab. The time |
-// it takes to negotiate a call isn't deterministic, but five seconds should be |
-// plenty of time. Similarly, the recording time should be enough to catch the |
-// whole reference file. If you then silence-trim the reference file and actual |
-// file, you should end up with two time-synchronized files. |
-void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( |
- const base::FilePath& reference_file, |
- const base::FilePath& recording, |
- const std::string& constraints, |
- const base::TimeDelta recording_time) { |
- ASSERT_TRUE(embedded_test_server()->Start()); |
- ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
- ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); |
- |
- ConfigureFakeDeviceToPlayFile(reference_file); |
- |
- // Create a two-way call. Mute one of the receivers though; that way it will |
- // be receiving audio bytes, but we will not be playing out of both elements. |
- GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); |
- content::WebContents* left_tab = |
- OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
- SetupPeerconnectionWithLocalStream(left_tab); |
- MuteMediaElement("remote-view", left_tab); |
- |
- content::WebContents* right_tab = |
- OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
- SetupPeerconnectionWithLocalStream(right_tab); |
- |
- AudioRecorder recorder; |
- ASSERT_TRUE(recorder.StartRecording(recording_time, recording)); |
- |
- NegotiateCall(left_tab, right_tab); |
- |
- ASSERT_TRUE(recorder.WaitForRecordingToEnd()); |
- DVLOG(0) << "Done recording to " << recording.value() << std::endl; |
- |
- HangUp(left_tab); |
-} |
- |
-void MAYBE_WebRtcAudioQualityBrowserTest::TestWithFakeDeviceGetUserMedia( |
- const std::string& constraints, |
- const std::string& perf_modifier) { |
- if (OnWin8()) { |
- // http://crbug.com/379798. |
- LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
- return; |
- } |
- |
- base::FilePath reference_file = |
- test::GetReferenceFilesDir().Append(kReferenceFile); |
- base::FilePath recording = CreateTemporaryWaveFile(); |
- |
- ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( |
- reference_file, recording, constraints, |
- base::TimeDelta::FromSeconds(30))); |
- |
- ComputeAndPrintPesqResults(reference_file, recording, perf_modifier); |
- DeleteFileUnlessTestFailed(recording, false); |
-} |
- |
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
- MANUAL_TestCallQualityWithAudioFromFakeDevice) { |
- TestWithFakeDeviceGetUserMedia(kAudioOnlyCallConstraints, "_getusermedia"); |
-} |
- |
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
- MANUAL_TestCallQualityWithAudioFromWebAudio) { |
- if (OnWin8()) { |
- // http://crbug.com/379798. |
- LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
- return; |
- } |
- ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
- ASSERT_TRUE(embedded_test_server()->Start()); |
- |
- ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); |
- |
- content::WebContents* left_tab = |
- OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
- content::WebContents* right_tab = |
- OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
- |
- AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); |
- |
- NegotiateCall(left_tab, right_tab); |
- |
- base::FilePath recording = CreateTemporaryWaveFile(); |
- |
- // Note: the sound clip is 21.6 seconds: record for 25 seconds to get some |
- // safety margins on each side. |
- AudioRecorder recorder; |
- ASSERT_TRUE(recorder.StartRecording(base::TimeDelta::FromSeconds(25), |
- recording)); |
- |
- PlayAudioFileThroughWebAudio(left_tab); |
- |
- ASSERT_TRUE(recorder.WaitForRecordingToEnd()); |
- DVLOG(0) << "Done recording to " << recording.value() << std::endl; |
- |
- HangUp(left_tab); |
- |
- // Compare with the reference file on disk (this is the same file we played |
- // through WebAudio earlier). |
- base::FilePath reference_file = |
- test::GetReferenceFilesDir().Append(kReferenceFile); |
- ComputeAndPrintPesqResults(reference_file, recording, "_webaudio"); |
-} |
- |
-/** |
- * The auto gain control test plays a file into the fake microphone. Then it |
- * sets up a one-way WebRTC call with audio only and records Chrome's output on |
- * the receiving side using the audio loopback provided by the quality test |
- * (see the class comments for more details). |
- * |
- * Then both the recording and reference file are split on silence. This creates |
- * a number of segments with speech in them. The reason for this is to provide |
- * a kind of synchronization mechanism so the start of each speech segment is |
- * compared to the start of the corresponding speech segment. This is because we |
- * will experience inevitable clock drift between the system clock (which runs |
- * the fake microphone) and the sound card (which runs play-out). Effectively |
- * re-synchronizing on each segment mitigates this. |
- * |
- * The silence splitting is inherently sensitive to the sound file we run on. |
- * Therefore the reference file must have at least 500 ms of pure silence |
- * between speech segments; the test will fail if the output produces more |
- * segments than the reference. |
- * |
- * The test reports the difference in decibel between the reference and output |
- * file per 10 ms interval in each speech segment. A value of 6 means the |
- * output was 6 dB louder than the reference, presumably because the AGC applied |
- * gain to the signal. |
- * |
- * The test only exercises digital AGC for now. |
- * |
- * We record in CD format here (44.1 kHz) because that's what the fake input |
- * device currently supports, and we want to be able to compare directly. See |
- * http://crbug.com/421054. |
- */ |
-void MAYBE_WebRtcAudioQualityBrowserTest::TestAutoGainControl( |
- const base::FilePath::StringType& reference_filename, |
- const std::string& constraints, |
- const std::string& perf_modifier) { |
- if (OnWin8()) { |
- // http://crbug.com/379798. |
- LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
- return; |
- } |
- base::FilePath reference_file = |
- test::GetReferenceFilesDir().Append(reference_filename); |
- base::FilePath recording = CreateTemporaryWaveFile(); |
- |
- ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( |
- reference_file, recording, constraints, |
- base::TimeDelta::FromSeconds(30))); |
- |
- base::ScopedTempDir split_ref_files; |
- ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); |
- ASSERT_NO_FATAL_FAILURE( |
- SplitFileOnSilenceIntoDir(reference_file, split_ref_files.path())); |
- std::vector<base::FilePath> ref_segments = |
- ListWavFilesInDir(split_ref_files.path()); |
- |
- base::ScopedTempDir split_actual_files; |
- ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); |
- ASSERT_NO_FATAL_FAILURE( |
- SplitFileOnSilenceIntoDir(recording, split_actual_files.path())); |
- |
- // Keep the recording and split files if the analysis fails. |
- base::FilePath actual_files_dir = split_actual_files.Take(); |
- std::vector<base::FilePath> actual_segments = |
- ListWavFilesInDir(actual_files_dir); |
- |
- AnalyzeSegmentsAndPrintResult( |
- ref_segments, actual_segments, reference_file, perf_modifier); |
- |
- DeleteFileUnlessTestFailed(recording, false); |
- DeleteFileUnlessTestFailed(actual_files_dir, true); |
-} |
- |
-// The AGC should apply non-zero gain here. |
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
- MANUAL_TestAutoGainControlOnLowAudio) { |
- ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
- kReferenceFile, kAudioOnlyCallConstraints, "_with_agc")); |
-} |
- |
-// The test is failing on the Win7 bot. |
-// http://crbug.com/625808#c23 |
-#if defined(OS_WIN) |
-#define MAYBE_MANUAL_TestAutoGainIsOffWithAudioProcessingOff\ |
- DISABLED_TestAutoGainIsOffWithAudioProcessingOff |
-#else |
-#define MAYBE_MANUAL_TestAutoGainIsOffWithAudioProcessingOff\ |
- MANUAL_TestAutoGainIsOffWithAudioProcessingOff |
-#endif |
-// Since the AGC is off here there should be no gain at all. |
-IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
- MAYBE_MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { |
- const char* kAudioCallWithoutAudioProcessing = |
- "{audio: { mandatory: { echoCancellation: false } } }"; |
- ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
- kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); |
-} |