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 <stddef.h> | 5 #include <stddef.h> |
6 | 6 |
7 #include <ctime> | 7 #include <ctime> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/files/file_enumerator.h" | 10 #include "base/files/file_enumerator.h" |
11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
12 #include "base/files/scoped_temp_dir.h" | 12 #include "base/files/scoped_temp_dir.h" |
13 #include "base/macros.h" | 13 #include "base/macros.h" |
14 #include "base/process/launch.h" | 14 #include "base/process/launch.h" |
15 #include "base/process/process.h" | 15 #include "base/process/process.h" |
16 #include "base/scoped_native_library.h" | 16 #include "base/scoped_native_library.h" |
17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
19 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
20 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" |
21 #include "base/test/test_file_util.h" | |
22 #include "build/build_config.h" | 21 #include "build/build_config.h" |
23 #include "chrome/browser/media/webrtc/webrtc_browsertest_audio.h" | 22 #include "chrome/browser/media/webrtc/webrtc_browsertest_audio.h" |
24 #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h" | 23 #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h" |
25 #include "chrome/browser/media/webrtc/webrtc_browsertest_common.h" | 24 #include "chrome/browser/media/webrtc/webrtc_browsertest_common.h" |
26 #include "chrome/browser/profiles/profile.h" | 25 #include "chrome/browser/profiles/profile.h" |
27 #include "chrome/browser/ui/browser.h" | 26 #include "chrome/browser/ui/browser.h" |
28 #include "chrome/browser/ui/browser_tabstrip.h" | 27 #include "chrome/browser/ui/browser_tabstrip.h" |
29 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 28 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
30 #include "chrome/common/chrome_paths.h" | 29 #include "chrome/common/chrome_paths.h" |
31 #include "chrome/common/chrome_switches.h" | 30 #include "chrome/common/chrome_switches.h" |
32 #include "chrome/common/pref_names.h" | |
33 #include "chrome/test/base/ui_test_utils.h" | 31 #include "chrome/test/base/ui_test_utils.h" |
34 #include "components/prefs/pref_service.h" | |
35 #include "content/public/common/content_switches.h" | 32 #include "content/public/common/content_switches.h" |
36 #include "content/public/test/browser_test_utils.h" | 33 #include "content/public/test/browser_test_utils.h" |
37 #include "media/base/audio_parameters.h" | 34 #include "media/base/audio_parameters.h" |
38 #include "media/base/media_switches.h" | 35 #include "media/base/media_switches.h" |
39 #include "net/test/embedded_test_server/embedded_test_server.h" | 36 #include "net/test/embedded_test_server/embedded_test_server.h" |
40 #include "testing/perf/perf_test.h" | 37 #include "testing/perf/perf_test.h" |
41 | 38 |
42 namespace { | 39 namespace { |
43 | 40 |
44 static const base::FilePath::CharType kReferenceFile[] = | 41 static const base::FilePath::CharType kReferenceFile[] = |
45 FILE_PATH_LITERAL("speech_44kHz_16bit_stereo.wav"); | 42 FILE_PATH_LITERAL("speech_44kHz_16bit_stereo.wav"); |
46 | 43 |
47 // The javascript will load the reference file relative to its location, | 44 // The javascript will load the reference file relative to its location, |
48 // which is in /webrtc on the web server. The files we are looking for are in | 45 // which is in /webrtc on the web server. The files we are looking for are in |
49 // webrtc/resources in the chrome/test/data folder. | 46 // webrtc/resources in the chrome/test/data folder. |
50 static const char kReferenceFileRelativeUrl[] = | 47 static const char kReferenceFileRelativeUrl[] = |
51 "resources/speech_44kHz_16bit_stereo.wav"; | 48 "resources/speech_44kHz_16bit_stereo.wav"; |
52 | 49 |
53 static const char kWebRtcAudioTestHtmlPage[] = | 50 static const char kWebRtcAudioTestHtmlPage[] = |
54 "/webrtc/webrtc_audio_quality_test.html"; | 51 "/webrtc/webrtc_audio_quality_test.html"; |
55 | 52 |
56 // How long to record the audio in the receiving peerConnection. | |
57 static const int kCaptureDurationInSeconds = 25; | |
58 | |
59 // The name where the recorded WebM audio file will be saved. | |
60 static const char kWebmRecordingFilename[] = "recording.webm"; | |
61 | |
62 // How often to ask the test page whether the audio recording is completed. | |
63 const int kPollingIntervalInMs = 1000; | |
64 | |
65 // For the AGC test, there are 6 speech segments split on silence. If one | 53 // For the AGC test, there are 6 speech segments split on silence. If one |
66 // segment is significantly different in length compared to the same segment in | 54 // segment is significantly different in length compared to the same segment in |
67 // the reference file, there's something fishy going on. | 55 // the reference file, there's something fishy going on. |
68 const int kMaxAgcSegmentDiffMs = | 56 const int kMaxAgcSegmentDiffMs = |
69 #if defined(OS_MACOSX) | 57 #if defined(OS_MACOSX) |
70 // Something is different on Mac; http://crbug.com/477653. | 58 // Something is different on Mac; http://crbug.com/477653. |
71 600; | 59 600; |
72 #else | 60 #else |
73 200; | 61 200; |
74 #endif | 62 #endif |
75 | 63 |
76 #if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MACOSX) | 64 #if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MACOSX) |
77 #define MAYBE_WebRtcAudioQualityBrowserTest WebRtcAudioQualityBrowserTest | 65 #define MAYBE_WebRtcAudioQualityBrowserTest WebRtcAudioQualityBrowserTest |
78 #else | 66 #else |
79 // Not implemented on Android, ChromeOS etc. | 67 // Not implemented on Android, ChromeOS etc. |
80 #define MAYBE_WebRtcAudioQualityBrowserTest DISABLED_WebRtcAudioQualityBrowserTe
st | 68 #define MAYBE_WebRtcAudioQualityBrowserTest DISABLED_WebRtcAudioQualityBrowserTe
st |
81 #endif | 69 #endif |
82 | 70 |
83 } // namespace | 71 } // namespace |
84 | 72 |
85 // Test we can set up a WebRTC call and play audio through it. | 73 // Test we can set up a WebRTC call and play audio through it. |
86 // | 74 // |
87 // If you're not a googler and want to run this test, you need to provide a | 75 // If you're not a googler and want to run this test, you need to provide a |
88 // pesq binary for your platform (and sox.exe on windows). Read more on how | 76 // pesq binary for your platform (and sox.exe on windows). Read more on how |
89 // resources are managed in chrome/test/data/webrtc/resources/README. | 77 // resources are managed in chrome/test/data/webrtc/resources/README. |
90 // | 78 // |
| 79 // This test will only work on machines that have been configured to record |
| 80 // their own input. |
| 81 // |
91 // On Linux: | 82 // On Linux: |
92 // 1. # sudo apt-get install sox | 83 // 1. # sudo apt-get install pavucontrol sox |
| 84 // 2. For the user who will run the test: # pavucontrol |
| 85 // 3. In a separate terminal, # arecord dummy |
| 86 // 4. In pavucontrol, go to the recording tab. |
| 87 // 5. For the ALSA plugin [aplay]: ALSA Capture from, change from <x> to |
| 88 // <Monitor of x>, where x is whatever your primary sound device is called. |
| 89 // 6. Try launching chrome as the target user on the target machine, try |
| 90 // playing, say, a YouTube video, and record with # arecord -f dat tmp.dat. |
| 91 // Verify the recording with aplay (should have recorded what you played |
| 92 // from chrome). |
| 93 // |
| 94 // Note: the volume for ALL your input devices will be forced to 100% by |
| 95 // running this test on Linux. |
93 // | 96 // |
94 // On Mac: | 97 // On Mac: |
95 // TODO(phoglund): download sox from gs instead. | 98 // TODO(phoglund): download sox from gs instead. |
96 // 1. Get SoundFlower: http://rogueamoeba.com/freebies/soundflower/download.php | 99 // 1. Get SoundFlower: http://rogueamoeba.com/freebies/soundflower/download.php |
97 // 2. Install it + reboot. | 100 // 2. Install it + reboot. |
98 // 3. Install MacPorts (http://www.macports.org/). | 101 // 3. Install MacPorts (http://www.macports.org/). |
99 // 4. Install sox: sudo port install sox. | 102 // 4. Install sox: sudo port install sox. |
100 // 5. (For Chrome bots) Ensure sox is reachable from the env the test | 103 // 5. (For Chrome bots) Ensure sox and rec are reachable from the env the test |
101 // executes in (sox tends to install in /opt/, which generally isn't in the | 104 // executes in (sox and rec tends to install in /opt/, which generally isn't |
102 // Chrome bots' env). For instance, run | 105 // in the Chrome bots' env). For instance, run |
| 106 // sudo ln -s /opt/local/bin/rec /usr/local/bin/rec |
103 // sudo ln -s /opt/local/bin/sox /usr/local/bin/sox | 107 // sudo ln -s /opt/local/bin/sox /usr/local/bin/sox |
| 108 // 6. In Sound Preferences, set both input and output to Soundflower (2ch). |
| 109 // Note: You will no longer hear audio on this machine, and it will no |
| 110 // longer use any built-in mics. |
| 111 // 7. Try launching chrome as the target user on the target machine, try |
| 112 // playing, say, a YouTube video, and record with 'rec test.wav trim 0 5'. |
| 113 // Stop the video in chrome and try playing back the file; you should hear |
| 114 // a recording of the video (note; if you play back on the target machine |
| 115 // you must revert the changes in step 3 first). |
| 116 // |
| 117 // On Windows 7: |
| 118 // 1. Control panel > Sound > Manage audio devices. |
| 119 // 2. In the recording tab, right-click in an empty space in the pane with the |
| 120 // devices. Tick 'show disabled devices'. |
| 121 // 3. You should see a 'stereo mix' device - this is what your speakers output. |
| 122 // If you don't have one, your driver doesn't support stereo mix devices. |
| 123 // Some drivers use different names for the mix device though (like "Wave"). |
| 124 // Right click > Properties. |
| 125 // 4. Ensure "listen to this device" is unchecked, otherwise you get echo. |
| 126 // 5. Ensure the mix device is the default recording device. |
| 127 // 6. Launch chrome and try playing a video with sound. You should see |
| 128 // in the volume meter for the mix device. Configure the mix device to have |
| 129 // 50 / 100 in level. Also go into the playback tab, right-click Speakers, |
| 130 // and set that level to 50 / 100. Otherwise you will get distortion in |
| 131 // the recording. |
104 class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { | 132 class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { |
105 public: | 133 public: |
106 MAYBE_WebRtcAudioQualityBrowserTest() {} | 134 MAYBE_WebRtcAudioQualityBrowserTest() {} |
107 void SetUpInProcessBrowserTestFixture() override { | 135 void SetUpInProcessBrowserTestFixture() override { |
108 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. | 136 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. |
109 } | 137 } |
110 | 138 |
111 void SetUpOnMainThread() override { | |
112 base::FilePath tmp_dir; | |
113 EXPECT_TRUE(base::GetTempDir(&tmp_dir)); | |
114 webm_recorded_output_filename_ = | |
115 tmp_dir.Append(FILE_PATH_LITERAL("recording.webm")); | |
116 | |
117 browser()->profile()->GetPrefs()->SetFilePath( | |
118 prefs::kDownloadDefaultDirectory, tmp_dir); | |
119 browser()->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload, | |
120 false); | |
121 } | |
122 | |
123 void SetUpCommandLine(base::CommandLine* command_line) override { | 139 void SetUpCommandLine(base::CommandLine* command_line) override { |
124 EXPECT_FALSE(command_line->HasSwitch( | 140 EXPECT_FALSE(command_line->HasSwitch( |
125 switches::kUseFakeUIForMediaStream)); | 141 switches::kUseFakeUIForMediaStream)); |
126 | 142 |
127 // The WebAudio-based tests don't care what devices are available to | 143 // The WebAudio-based tests don't care what devices are available to |
128 // getUserMedia, and the getUserMedia-based tests will play back a file | 144 // getUserMedia, and the getUserMedia-based tests will play back a file |
129 // through the fake device using using --use-file-for-fake-audio-capture. | 145 // through the fake device using using --use-file-for-fake-audio-capture. |
130 command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream); | 146 command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream); |
131 | 147 |
132 // Add loopback interface such that there is always connectivity. | 148 // Add loopback interface such that there is always connectivity. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
168 content::WebContents* tab_contents) { | 184 content::WebContents* tab_contents) { |
169 EXPECT_EQ("ok-muted", ExecuteJavascript( | 185 EXPECT_EQ("ok-muted", ExecuteJavascript( |
170 "setMediaElementMuted('" + element_id + "', true)", tab_contents)); | 186 "setMediaElementMuted('" + element_id + "', true)", tab_contents)); |
171 } | 187 } |
172 | 188 |
173 protected: | 189 protected: |
174 void TestAutoGainControl(const base::FilePath::StringType& reference_filename, | 190 void TestAutoGainControl(const base::FilePath::StringType& reference_filename, |
175 const std::string& constraints, | 191 const std::string& constraints, |
176 const std::string& perf_modifier); | 192 const std::string& perf_modifier); |
177 void SetupAndRecordAudioCall(const base::FilePath& reference_file, | 193 void SetupAndRecordAudioCall(const base::FilePath& reference_file, |
178 const base::FilePath& recorded_output_path, | 194 const base::FilePath& recording, |
179 const std::string& constraints); | 195 const std::string& constraints, |
| 196 const base::TimeDelta recording_time); |
180 void TestWithFakeDeviceGetUserMedia(const std::string& constraints, | 197 void TestWithFakeDeviceGetUserMedia(const std::string& constraints, |
181 const std::string& perf_modifier); | 198 const std::string& perf_modifier); |
182 | |
183 base::FilePath webm_recorded_output_filename_; | |
184 }; | 199 }; |
185 | 200 |
186 namespace { | 201 namespace { |
187 | 202 |
| 203 class AudioRecorder { |
| 204 public: |
| 205 AudioRecorder() {} |
| 206 ~AudioRecorder() {} |
| 207 |
| 208 // Starts the recording program for the specified duration. Returns true |
| 209 // on success. We record in 16-bit 44.1 kHz Stereo (mostly because that's |
| 210 // what SoundRecorder.exe will give us and we can't change that). |
| 211 bool StartRecording(base::TimeDelta recording_time, |
| 212 const base::FilePath& output_file) { |
| 213 EXPECT_FALSE(recording_application_.IsValid()) |
| 214 << "Tried to record, but is already recording."; |
| 215 |
| 216 int duration_sec = static_cast<int>(recording_time.InSeconds()); |
| 217 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); |
| 218 |
| 219 #if defined(OS_WIN) |
| 220 // This disable is required to run SoundRecorder.exe on 64-bit Windows |
| 221 // from a 32-bit binary. We need to load the wow64 disable function from |
| 222 // the DLL since it doesn't exist on Windows XP. |
| 223 base::ScopedNativeLibrary kernel32_lib(base::FilePath(L"kernel32")); |
| 224 if (kernel32_lib.is_valid()) { |
| 225 typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*); |
| 226 Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection; |
| 227 wow_64_disable_wow_64_fs_redirection = |
| 228 reinterpret_cast<Wow64DisableWow64FSRedirection>( |
| 229 kernel32_lib.GetFunctionPointer( |
| 230 "Wow64DisableWow64FsRedirection")); |
| 231 if (wow_64_disable_wow_64_fs_redirection != NULL) { |
| 232 PVOID* ignored = NULL; |
| 233 wow_64_disable_wow_64_fs_redirection(ignored); |
| 234 } |
| 235 } |
| 236 |
| 237 char duration_in_hms[128] = {0}; |
| 238 struct tm duration_tm = {0}; |
| 239 duration_tm.tm_sec = duration_sec; |
| 240 EXPECT_NE(0u, strftime(duration_in_hms, arraysize(duration_in_hms), |
| 241 "%H:%M:%S", &duration_tm)); |
| 242 |
| 243 command_line.SetProgram( |
| 244 base::FilePath(FILE_PATH_LITERAL("SoundRecorder.exe"))); |
| 245 command_line.AppendArg("/FILE"); |
| 246 command_line.AppendArgPath(output_file); |
| 247 command_line.AppendArg("/DURATION"); |
| 248 command_line.AppendArg(duration_in_hms); |
| 249 #elif defined(OS_MACOSX) |
| 250 command_line.SetProgram(base::FilePath("rec")); |
| 251 command_line.AppendArg("-b"); |
| 252 command_line.AppendArg("16"); |
| 253 command_line.AppendArg("-q"); |
| 254 command_line.AppendArgPath(output_file); |
| 255 command_line.AppendArg("trim"); |
| 256 command_line.AppendArg("0"); |
| 257 command_line.AppendArg(base::IntToString(duration_sec)); |
| 258 #else |
| 259 command_line.SetProgram(base::FilePath("arecord")); |
| 260 command_line.AppendArg("-d"); |
| 261 command_line.AppendArg(base::IntToString(duration_sec)); |
| 262 command_line.AppendArg("-f"); |
| 263 command_line.AppendArg("cd"); |
| 264 command_line.AppendArg("-c"); |
| 265 command_line.AppendArg("2"); |
| 266 command_line.AppendArgPath(output_file); |
| 267 #endif |
| 268 |
| 269 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
| 270 recording_application_ = |
| 271 base::LaunchProcess(command_line, base::LaunchOptions()); |
| 272 return recording_application_.IsValid(); |
| 273 } |
| 274 |
| 275 // Joins the recording program. Returns true on success. |
| 276 bool WaitForRecordingToEnd() { |
| 277 int exit_code = -1; |
| 278 recording_application_.WaitForExit(&exit_code); |
| 279 return exit_code == 0; |
| 280 } |
| 281 private: |
| 282 base::Process recording_application_; |
| 283 }; |
| 284 |
| 285 bool ForceMicrophoneVolumeTo100Percent() { |
| 286 #if defined(OS_WIN) |
| 287 // Note: the force binary isn't in tools since it's one of our own. |
| 288 base::CommandLine command_line(test::GetReferenceFilesDir().Append( |
| 289 FILE_PATH_LITERAL("force_mic_volume_max.exe"))); |
| 290 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
| 291 std::string result; |
| 292 if (!base::GetAppOutput(command_line, &result)) { |
| 293 LOG(ERROR) << "Failed to set source volume: output was " << result; |
| 294 return false; |
| 295 } |
| 296 #elif defined(OS_MACOSX) |
| 297 base::CommandLine command_line( |
| 298 base::FilePath(FILE_PATH_LITERAL("osascript"))); |
| 299 command_line.AppendArg("-e"); |
| 300 command_line.AppendArg("set volume input volume 100"); |
| 301 command_line.AppendArg("-e"); |
| 302 command_line.AppendArg("set volume output volume 85"); |
| 303 |
| 304 std::string result; |
| 305 if (!base::GetAppOutput(command_line, &result)) { |
| 306 LOG(ERROR) << "Failed to set source volume: output was " << result; |
| 307 return false; |
| 308 } |
| 309 #else |
| 310 // Just force the volume of, say the first 5 devices. A machine will rarely |
| 311 // have more input sources than that. This is way easier than finding the |
| 312 // input device we happen to be using. |
| 313 for (int device_index = 0; device_index < 5; ++device_index) { |
| 314 std::string result; |
| 315 const std::string kHundredPercentVolume = "65536"; |
| 316 base::CommandLine command_line(base::FilePath(FILE_PATH_LITERAL("pacmd"))); |
| 317 command_line.AppendArg("set-source-volume"); |
| 318 command_line.AppendArg(base::IntToString(device_index)); |
| 319 command_line.AppendArg(kHundredPercentVolume); |
| 320 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
| 321 if (!base::GetAppOutput(command_line, &result)) { |
| 322 LOG(ERROR) << "Failed to set source volume: output was " << result; |
| 323 return false; |
| 324 } |
| 325 } |
| 326 #endif |
| 327 return true; |
| 328 } |
| 329 |
188 // Sox is the "Swiss army knife" of audio processing. We mainly use it for | 330 // Sox is the "Swiss army knife" of audio processing. We mainly use it for |
189 // silence trimming. See http://sox.sourceforge.net. | 331 // silence trimming. See http://sox.sourceforge.net. |
190 base::CommandLine MakeSoxCommandLine() { | 332 base::CommandLine MakeSoxCommandLine() { |
191 #if defined(OS_WIN) | 333 #if defined(OS_WIN) |
192 base::FilePath sox_path = test::GetToolForPlatform("sox"); | 334 base::FilePath sox_path = test::GetToolForPlatform("sox"); |
193 if (!base::PathExists(sox_path)) { | 335 if (!base::PathExists(sox_path)) { |
194 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() | 336 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() |
195 << "; you may have to provide this binary yourself."; | 337 << "; you may have to provide this binary yourself."; |
196 return base::CommandLine(base::CommandLine::NO_PROGRAM); | 338 return base::CommandLine(base::CommandLine::NO_PROGRAM); |
197 } | 339 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 command_line.AppendArg(kDuration); | 379 command_line.AppendArg(kDuration); |
238 command_line.AppendArg(kTreshold); | 380 command_line.AppendArg(kTreshold); |
239 command_line.AppendArg("reverse"); | 381 command_line.AppendArg("reverse"); |
240 | 382 |
241 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | 383 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
242 std::string result; | 384 std::string result; |
243 bool ok = base::GetAppOutput(command_line, &result); | 385 bool ok = base::GetAppOutput(command_line, &result); |
244 DVLOG(0) << "Output was:\n\n" << result; | 386 DVLOG(0) << "Output was:\n\n" << result; |
245 return ok; | 387 return ok; |
246 } | 388 } |
247 | |
248 // Runs ffmpeg on the captured webm video and writes it to a .wav file. | |
249 bool RunWebmToWavConverter(const base::FilePath& webm_recorded_output_path, | |
250 const base::FilePath& wav_recorded_output_path) { | |
251 const base::FilePath path_to_ffmpeg = test::GetToolForPlatform("ffmpeg"); | |
252 if (!base::PathExists(path_to_ffmpeg)) { | |
253 LOG(ERROR) << "Missing ffmpeg: should be in " << path_to_ffmpeg.value(); | |
254 return false; | |
255 } | |
256 | |
257 // Set up ffmpeg to output at a certain bitrate (-ab). This is hopefully set | |
258 // high enough to avoid degrading audio quality too much. | |
259 base::CommandLine ffmpeg_command(path_to_ffmpeg); | |
260 ffmpeg_command.AppendArg("-i"); | |
261 ffmpeg_command.AppendArgPath(webm_recorded_output_path); | |
262 ffmpeg_command.AppendArg("-ab"); | |
263 ffmpeg_command.AppendArg("300k"); | |
264 ffmpeg_command.AppendArg("-y"); | |
265 ffmpeg_command.AppendArgPath(wav_recorded_output_path); | |
266 | |
267 // We produce an output file that will later be used as an input to the | |
268 // barcode decoder and frame analyzer tools. | |
269 DVLOG(0) << "Running " << ffmpeg_command.GetCommandLineString(); | |
270 std::string result; | |
271 bool ok = base::GetAppOutputAndError(ffmpeg_command, &result); | |
272 DVLOG(0) << "Output was:\n\n" << result; | |
273 return ok; | |
274 } | |
275 | 389 |
276 // Looks for 0.2 second audio segments surrounded by silences under 0.3% audio | 390 // Looks for 0.2 second audio segments surrounded by silences under 0.3% audio |
277 // power and splits the input file on those silences. Output files are written | 391 // power and splits the input file on those silences. Output files are written |
278 // according to the output file template (e.g. /tmp/out.wav writes | 392 // according to the output file template (e.g. /tmp/out.wav writes |
279 // /tmp/out001.wav, /tmp/out002.wav, etc if there are two silence-padded | 393 // /tmp/out001.wav, /tmp/out002.wav, etc if there are two silence-padded |
280 // regions in the file). The silences between speech segments must be at | 394 // regions in the file). The silences between speech segments must be at |
281 // least 500 ms for this to be reliable. | 395 // least 500 ms for this to be reliable. |
282 bool SplitFileOnSilence(const base::FilePath& input_file, | 396 bool SplitFileOnSilence(const base::FilePath& input_file, |
283 const base::FilePath& output_file_template) { | 397 const base::FilePath& output_file_template) { |
284 base::CommandLine command_line = MakeSoxCommandLine(); | 398 base::CommandLine command_line = MakeSoxCommandLine(); |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
466 float difference_in_decibel = AnalyzeOneSegment(ref_segments[i], | 580 float difference_in_decibel = AnalyzeOneSegment(ref_segments[i], |
467 actual_segments[i], | 581 actual_segments[i], |
468 i); | 582 i); |
469 std::string trace_name = MakeTraceName(reference_file, i); | 583 std::string trace_name = MakeTraceName(reference_file, i); |
470 perf_test::PrintResult("agc_energy_diff", perf_modifier, trace_name, | 584 perf_test::PrintResult("agc_energy_diff", perf_modifier, trace_name, |
471 difference_in_decibel, "dB", false); | 585 difference_in_decibel, "dB", false); |
472 } | 586 } |
473 } | 587 } |
474 | 588 |
475 void ComputeAndPrintPesqResults(const base::FilePath& reference_file, | 589 void ComputeAndPrintPesqResults(const base::FilePath& reference_file, |
476 const base::FilePath& recorded_output_path, | 590 const base::FilePath& recording, |
477 const std::string& perf_modifier) { | 591 const std::string& perf_modifier) { |
478 base::FilePath trimmed_reference = CreateTemporaryWaveFile(); | 592 base::FilePath trimmed_reference = CreateTemporaryWaveFile(); |
479 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); | 593 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); |
480 | 594 |
481 ASSERT_TRUE(RemoveSilence(reference_file, trimmed_reference)); | 595 ASSERT_TRUE(RemoveSilence(reference_file, trimmed_reference)); |
482 ASSERT_TRUE(RemoveSilence(recorded_output_path, trimmed_recording)); | 596 ASSERT_TRUE(RemoveSilence(recording, trimmed_recording)); |
483 | 597 |
484 std::string raw_mos; | 598 std::string raw_mos; |
485 std::string mos_lqo; | 599 std::string mos_lqo; |
486 bool succeeded = RunPesq(trimmed_reference, trimmed_recording, 16000, | 600 bool succeeded = RunPesq(trimmed_reference, trimmed_recording, 16000, |
487 &raw_mos, &mos_lqo); | 601 &raw_mos, &mos_lqo); |
488 EXPECT_TRUE(succeeded) << "Failed to run PESQ."; | 602 EXPECT_TRUE(succeeded) << "Failed to run PESQ."; |
489 if (succeeded) { | 603 if (succeeded) { |
490 perf_test::PrintResult( | 604 perf_test::PrintResult( |
491 "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); | 605 "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); |
492 perf_test::PrintResult( | 606 perf_test::PrintResult( |
493 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); | 607 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); |
494 } | 608 } |
495 | 609 |
496 DeleteFileUnlessTestFailed(trimmed_reference, false); | 610 DeleteFileUnlessTestFailed(trimmed_reference, false); |
497 DeleteFileUnlessTestFailed(trimmed_recording, false); | 611 DeleteFileUnlessTestFailed(trimmed_recording, false); |
498 } | 612 } |
499 | 613 |
500 } // namespace | 614 } // namespace |
501 | 615 |
502 // Sets up a two-way WebRTC call and records its output to | 616 // Sets up a two-way WebRTC call and records its output to |recording|, using |
503 // |recorded_output_path|, using getUserMedia. | 617 // getUserMedia. |
504 // | 618 // |
505 // |reference_file| should have at least five seconds of silence in the | 619 // |reference_file| should have at least five seconds of silence in the |
506 // beginning: otherwise all the reference audio will not be picked up by the | 620 // beginning: otherwise all the reference audio will not be picked up by the |
507 // recording. Note that the reference file will start playing as soon as the | 621 // recording. Note that the reference file will start playing as soon as the |
508 // audio device is up following the getUserMedia call in the left tab. The time | 622 // audio device is up following the getUserMedia call in the left tab. The time |
509 // it takes to negotiate a call isn't deterministic, but five seconds should be | 623 // it takes to negotiate a call isn't deterministic, but five seconds should be |
510 // plenty of time. Similarly, the recording time should be enough to catch the | 624 // plenty of time. Similarly, the recording time should be enough to catch the |
511 // whole reference file. If you then silence-trim the reference file and actual | 625 // whole reference file. If you then silence-trim the reference file and actual |
512 // file, you should end up with two time-synchronized files. | 626 // file, you should end up with two time-synchronized files. |
513 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( | 627 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( |
514 const base::FilePath& reference_file, | 628 const base::FilePath& reference_file, |
515 const base::FilePath& recorded_output_path, | 629 const base::FilePath& recording, |
516 const std::string& constraints) { | 630 const std::string& constraints, |
| 631 const base::TimeDelta recording_time) { |
517 ASSERT_TRUE(embedded_test_server()->Start()); | 632 ASSERT_TRUE(embedded_test_server()->Start()); |
518 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 633 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
| 634 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); |
519 | 635 |
520 ConfigureFakeDeviceToPlayFile(reference_file); | 636 ConfigureFakeDeviceToPlayFile(reference_file); |
521 | 637 |
522 // Create a two-way call. Mute one of the receivers though; that way it will | 638 // Create a two-way call. Mute one of the receivers though; that way it will |
523 // be receiving audio bytes, but we will not be playing out of both elements. | 639 // be receiving audio bytes, but we will not be playing out of both elements. |
524 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); | 640 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); |
525 content::WebContents* left_tab = | 641 content::WebContents* left_tab = |
526 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); | 642 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
527 SetupPeerconnectionWithLocalStream(left_tab); | 643 SetupPeerconnectionWithLocalStream(left_tab); |
528 MuteMediaElement("remote-view", left_tab); | 644 MuteMediaElement("remote-view", left_tab); |
529 | 645 |
530 content::WebContents* right_tab = | 646 content::WebContents* right_tab = |
531 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); | 647 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
532 SetupPeerconnectionWithLocalStream(right_tab); | 648 SetupPeerconnectionWithLocalStream(right_tab); |
533 | 649 |
| 650 AudioRecorder recorder; |
| 651 ASSERT_TRUE(recorder.StartRecording(recording_time, recording)); |
| 652 |
534 NegotiateCall(left_tab, right_tab); | 653 NegotiateCall(left_tab, right_tab); |
535 | 654 |
536 EXPECT_EQ( | 655 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); |
537 "ok-capturing", | 656 DVLOG(0) << "Done recording to " << recording.value() << std::endl; |
538 ExecuteJavascript( | |
539 base::StringPrintf("startAudioCapture(%d, \"%s\");", | |
540 kCaptureDurationInSeconds, kWebmRecordingFilename), | |
541 right_tab)); | |
542 | |
543 EXPECT_TRUE(test::PollingWaitUntil("testIsDoneCapturing();", "true", | |
544 right_tab, kPollingIntervalInMs)); | |
545 | 657 |
546 HangUp(left_tab); | 658 HangUp(left_tab); |
547 | |
548 RunWebmToWavConverter(webm_recorded_output_filename_, recorded_output_path); | |
549 EXPECT_TRUE(base::DieFileDie(webm_recorded_output_filename_, false)); | |
550 | |
551 DVLOG(0) << "Done recording to " << recorded_output_path.MaybeAsASCII(); | |
552 } | 659 } |
553 | 660 |
554 void MAYBE_WebRtcAudioQualityBrowserTest::TestWithFakeDeviceGetUserMedia( | 661 void MAYBE_WebRtcAudioQualityBrowserTest::TestWithFakeDeviceGetUserMedia( |
555 const std::string& constraints, | 662 const std::string& constraints, |
556 const std::string& perf_modifier) { | 663 const std::string& perf_modifier) { |
557 if (OnWin8()) { | 664 if (OnWin8()) { |
558 // http://crbug.com/379798. | 665 // http://crbug.com/379798. |
559 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 666 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
560 return; | 667 return; |
561 } | 668 } |
562 | 669 |
563 base::FilePath reference_file = | 670 base::FilePath reference_file = |
564 test::GetReferenceFilesDir().Append(kReferenceFile); | 671 test::GetReferenceFilesDir().Append(kReferenceFile); |
565 base::FilePath recorded_output_path = CreateTemporaryWaveFile(); | 672 base::FilePath recording = CreateTemporaryWaveFile(); |
566 | 673 |
567 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( | 674 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( |
568 reference_file, recorded_output_path, constraints)); | 675 reference_file, recording, constraints, |
| 676 base::TimeDelta::FromSeconds(30))); |
569 | 677 |
570 ComputeAndPrintPesqResults(reference_file, recorded_output_path, | 678 ComputeAndPrintPesqResults(reference_file, recording, perf_modifier); |
571 perf_modifier); | 679 DeleteFileUnlessTestFailed(recording, false); |
572 DeleteFileUnlessTestFailed(recorded_output_path, false); | |
573 } | 680 } |
574 | 681 |
575 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 682 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
576 MANUAL_TestCallQualityWithAudioFromFakeDevice) { | 683 MANUAL_TestCallQualityWithAudioFromFakeDevice) { |
577 TestWithFakeDeviceGetUserMedia(kAudioOnlyCallConstraints, "_getusermedia"); | 684 TestWithFakeDeviceGetUserMedia(kAudioOnlyCallConstraints, "_getusermedia"); |
578 } | 685 } |
579 | 686 |
580 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 687 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
581 MANUAL_TestCallQualityWithAudioFromWebAudio) { | 688 MANUAL_TestCallQualityWithAudioFromWebAudio) { |
582 if (OnWin8()) { | 689 if (OnWin8()) { |
583 // http://crbug.com/379798. | 690 // http://crbug.com/379798. |
584 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 691 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
585 return; | 692 return; |
586 } | 693 } |
587 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 694 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
588 ASSERT_TRUE(embedded_test_server()->Start()); | 695 ASSERT_TRUE(embedded_test_server()->Start()); |
589 | 696 |
| 697 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); |
| 698 |
590 content::WebContents* left_tab = | 699 content::WebContents* left_tab = |
591 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 700 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
592 content::WebContents* right_tab = | 701 content::WebContents* right_tab = |
593 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 702 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
594 | 703 |
595 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); | 704 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); |
596 | 705 |
597 NegotiateCall(left_tab, right_tab); | 706 NegotiateCall(left_tab, right_tab); |
598 | 707 |
599 const base::FilePath recorded_output_path = CreateTemporaryWaveFile(); | 708 base::FilePath recording = CreateTemporaryWaveFile(); |
| 709 |
| 710 // Note: the sound clip is 21.6 seconds: record for 25 seconds to get some |
| 711 // safety margins on each side. |
| 712 AudioRecorder recorder; |
| 713 ASSERT_TRUE(recorder.StartRecording(base::TimeDelta::FromSeconds(25), |
| 714 recording)); |
600 | 715 |
601 PlayAudioFileThroughWebAudio(left_tab); | 716 PlayAudioFileThroughWebAudio(left_tab); |
602 | 717 |
603 EXPECT_EQ( | 718 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); |
604 "ok-capturing", | 719 DVLOG(0) << "Done recording to " << recording.value() << std::endl; |
605 ExecuteJavascript( | |
606 base::StringPrintf("startAudioCapture(%d, \"%s\");", | |
607 kCaptureDurationInSeconds, kWebmRecordingFilename), | |
608 right_tab)); | |
609 | |
610 EXPECT_TRUE(test::PollingWaitUntil("testIsDoneCapturing();", "true", | |
611 right_tab, kPollingIntervalInMs)); | |
612 | 720 |
613 HangUp(left_tab); | 721 HangUp(left_tab); |
614 | 722 |
615 RunWebmToWavConverter(webm_recorded_output_filename_, recorded_output_path); | |
616 EXPECT_TRUE(base::DieFileDie(webm_recorded_output_filename_, false)); | |
617 | |
618 DVLOG(0) << "Done recording to " << recorded_output_path.MaybeAsASCII(); | |
619 | |
620 // Compare with the reference file on disk (this is the same file we played | 723 // Compare with the reference file on disk (this is the same file we played |
621 // through WebAudio earlier). | 724 // through WebAudio earlier). |
622 base::FilePath reference_file = | 725 base::FilePath reference_file = |
623 test::GetReferenceFilesDir().Append(kReferenceFile); | 726 test::GetReferenceFilesDir().Append(kReferenceFile); |
624 ComputeAndPrintPesqResults(reference_file, recorded_output_path, "_webaudio"); | 727 ComputeAndPrintPesqResults(reference_file, recording, "_webaudio"); |
625 } | 728 } |
626 | 729 |
627 /** | 730 /** |
628 * The auto gain control test plays a file into the fake microphone. Then it | 731 * The auto gain control test plays a file into the fake microphone. Then it |
629 * sets up a one-way WebRTC call with audio only and records Chrome's output on | 732 * sets up a one-way WebRTC call with audio only and records Chrome's output on |
630 * the receiving side using the audio loopback provided by the quality test | 733 * the receiving side using the audio loopback provided by the quality test |
631 * (see the class comments for more details). | 734 * (see the class comments for more details). |
632 * | 735 * |
633 * Then both the recording and reference file are split on silence. This creates | 736 * Then both the recording and reference file are split on silence. This creates |
634 * a number of segments with speech in them. The reason for this is to provide | 737 * a number of segments with speech in them. The reason for this is to provide |
(...skipping 23 matching lines...) Expand all Loading... |
658 const base::FilePath::StringType& reference_filename, | 761 const base::FilePath::StringType& reference_filename, |
659 const std::string& constraints, | 762 const std::string& constraints, |
660 const std::string& perf_modifier) { | 763 const std::string& perf_modifier) { |
661 if (OnWin8()) { | 764 if (OnWin8()) { |
662 // http://crbug.com/379798. | 765 // http://crbug.com/379798. |
663 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 766 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
664 return; | 767 return; |
665 } | 768 } |
666 base::FilePath reference_file = | 769 base::FilePath reference_file = |
667 test::GetReferenceFilesDir().Append(reference_filename); | 770 test::GetReferenceFilesDir().Append(reference_filename); |
668 base::FilePath recorded_output_path = CreateTemporaryWaveFile(); | 771 base::FilePath recording = CreateTemporaryWaveFile(); |
669 | 772 |
670 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( | 773 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( |
671 reference_file, recorded_output_path, constraints)); | 774 reference_file, recording, constraints, |
| 775 base::TimeDelta::FromSeconds(30))); |
672 | 776 |
673 base::ScopedTempDir split_ref_files; | 777 base::ScopedTempDir split_ref_files; |
674 ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); | 778 ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); |
675 ASSERT_NO_FATAL_FAILURE( | 779 ASSERT_NO_FATAL_FAILURE( |
676 SplitFileOnSilenceIntoDir(reference_file, split_ref_files.GetPath())); | 780 SplitFileOnSilenceIntoDir(reference_file, split_ref_files.GetPath())); |
677 std::vector<base::FilePath> ref_segments = | 781 std::vector<base::FilePath> ref_segments = |
678 ListWavFilesInDir(split_ref_files.GetPath()); | 782 ListWavFilesInDir(split_ref_files.GetPath()); |
679 | 783 |
680 base::ScopedTempDir split_actual_files; | 784 base::ScopedTempDir split_actual_files; |
681 ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); | 785 ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); |
682 ASSERT_NO_FATAL_FAILURE(SplitFileOnSilenceIntoDir( | 786 ASSERT_NO_FATAL_FAILURE( |
683 recorded_output_path, split_actual_files.GetPath())); | 787 SplitFileOnSilenceIntoDir(recording, split_actual_files.GetPath())); |
684 | 788 |
685 // Keep the recording and split files if the analysis fails. | 789 // Keep the recording and split files if the analysis fails. |
686 base::FilePath actual_files_dir = split_actual_files.Take(); | 790 base::FilePath actual_files_dir = split_actual_files.Take(); |
687 std::vector<base::FilePath> actual_segments = | 791 std::vector<base::FilePath> actual_segments = |
688 ListWavFilesInDir(actual_files_dir); | 792 ListWavFilesInDir(actual_files_dir); |
689 | 793 |
690 AnalyzeSegmentsAndPrintResult( | 794 AnalyzeSegmentsAndPrintResult( |
691 ref_segments, actual_segments, reference_file, perf_modifier); | 795 ref_segments, actual_segments, reference_file, perf_modifier); |
692 | 796 |
693 DeleteFileUnlessTestFailed(recorded_output_path, false); | 797 DeleteFileUnlessTestFailed(recording, false); |
694 DeleteFileUnlessTestFailed(actual_files_dir, true); | 798 DeleteFileUnlessTestFailed(actual_files_dir, true); |
695 } | 799 } |
696 | 800 |
697 // The AGC should apply non-zero gain here. | 801 // The AGC should apply non-zero gain here. |
698 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 802 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
699 MANUAL_TestAutoGainControlOnLowAudio) { | 803 MANUAL_TestAutoGainControlOnLowAudio) { |
700 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | 804 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
701 kReferenceFile, kAudioOnlyCallConstraints, "_with_agc")); | 805 kReferenceFile, kAudioOnlyCallConstraints, "_with_agc")); |
702 } | 806 } |
703 | 807 |
704 // Since the AGC is off here there should be no gain at all. | 808 // Since the AGC is off here there should be no gain at all. |
705 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 809 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
706 MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { | 810 MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { |
707 const char* kAudioCallWithoutAudioProcessing = | 811 const char* kAudioCallWithoutAudioProcessing = |
708 "{audio: { mandatory: { echoCancellation: false } } }"; | 812 "{audio: { mandatory: { echoCancellation: false } } }"; |
709 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | 813 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
710 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); | 814 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); |
711 } | 815 } |
OLD | NEW |