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" |
21 #include "build/build_config.h" | 22 #include "build/build_config.h" |
22 #include "chrome/browser/media/webrtc/webrtc_browsertest_audio.h" | 23 #include "chrome/browser/media/webrtc/webrtc_browsertest_audio.h" |
23 #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h" | 24 #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h" |
24 #include "chrome/browser/media/webrtc/webrtc_browsertest_common.h" | 25 #include "chrome/browser/media/webrtc/webrtc_browsertest_common.h" |
25 #include "chrome/browser/profiles/profile.h" | 26 #include "chrome/browser/profiles/profile.h" |
26 #include "chrome/browser/ui/browser.h" | 27 #include "chrome/browser/ui/browser.h" |
27 #include "chrome/browser/ui/browser_tabstrip.h" | 28 #include "chrome/browser/ui/browser_tabstrip.h" |
28 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 29 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
29 #include "chrome/common/chrome_paths.h" | 30 #include "chrome/common/chrome_paths.h" |
30 #include "chrome/common/chrome_switches.h" | 31 #include "chrome/common/chrome_switches.h" |
| 32 #include "chrome/common/pref_names.h" |
31 #include "chrome/test/base/ui_test_utils.h" | 33 #include "chrome/test/base/ui_test_utils.h" |
| 34 #include "components/prefs/pref_service.h" |
32 #include "content/public/common/content_switches.h" | 35 #include "content/public/common/content_switches.h" |
33 #include "content/public/test/browser_test_utils.h" | 36 #include "content/public/test/browser_test_utils.h" |
34 #include "media/base/audio_parameters.h" | 37 #include "media/base/audio_parameters.h" |
35 #include "media/base/media_switches.h" | 38 #include "media/base/media_switches.h" |
36 #include "net/test/embedded_test_server/embedded_test_server.h" | 39 #include "net/test/embedded_test_server/embedded_test_server.h" |
37 #include "testing/perf/perf_test.h" | 40 #include "testing/perf/perf_test.h" |
38 | 41 |
39 namespace { | 42 namespace { |
40 | 43 |
41 static const base::FilePath::CharType kReferenceFile[] = | 44 static const base::FilePath::CharType kReferenceFile[] = |
42 FILE_PATH_LITERAL("speech_44kHz_16bit_stereo.wav"); | 45 FILE_PATH_LITERAL("speech_44kHz_16bit_stereo.wav"); |
43 | 46 |
44 // The javascript will load the reference file relative to its location, | 47 // The javascript will load the reference file relative to its location, |
45 // which is in /webrtc on the web server. The files we are looking for are in | 48 // which is in /webrtc on the web server. The files we are looking for are in |
46 // webrtc/resources in the chrome/test/data folder. | 49 // webrtc/resources in the chrome/test/data folder. |
47 static const char kReferenceFileRelativeUrl[] = | 50 static const char kReferenceFileRelativeUrl[] = |
48 "resources/speech_44kHz_16bit_stereo.wav"; | 51 "resources/speech_44kHz_16bit_stereo.wav"; |
49 | 52 |
50 static const char kWebRtcAudioTestHtmlPage[] = | 53 static const char kWebRtcAudioTestHtmlPage[] = |
51 "/webrtc/webrtc_audio_quality_test.html"; | 54 "/webrtc/webrtc_audio_quality_test.html"; |
52 | 55 |
| 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 |
53 // For the AGC test, there are 6 speech segments split on silence. If one | 65 // For the AGC test, there are 6 speech segments split on silence. If one |
54 // segment is significantly different in length compared to the same segment in | 66 // segment is significantly different in length compared to the same segment in |
55 // the reference file, there's something fishy going on. | 67 // the reference file, there's something fishy going on. |
56 const int kMaxAgcSegmentDiffMs = | 68 const int kMaxAgcSegmentDiffMs = |
57 #if defined(OS_MACOSX) | 69 #if defined(OS_MACOSX) |
58 // Something is different on Mac; http://crbug.com/477653. | 70 // Something is different on Mac; http://crbug.com/477653. |
59 600; | 71 600; |
60 #else | 72 #else |
61 200; | 73 200; |
62 #endif | 74 #endif |
63 | 75 |
64 #if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MACOSX) | 76 #if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MACOSX) |
65 #define MAYBE_WebRtcAudioQualityBrowserTest WebRtcAudioQualityBrowserTest | 77 #define MAYBE_WebRtcAudioQualityBrowserTest WebRtcAudioQualityBrowserTest |
66 #else | 78 #else |
67 // Not implemented on Android, ChromeOS etc. | 79 // Not implemented on Android, ChromeOS etc. |
68 #define MAYBE_WebRtcAudioQualityBrowserTest DISABLED_WebRtcAudioQualityBrowserTe
st | 80 #define MAYBE_WebRtcAudioQualityBrowserTest DISABLED_WebRtcAudioQualityBrowserTe
st |
69 #endif | 81 #endif |
70 | 82 |
71 } // namespace | 83 } // namespace |
72 | 84 |
73 // Test we can set up a WebRTC call and play audio through it. | 85 // Test we can set up a WebRTC call and play audio through it. |
74 // | 86 // |
75 // If you're not a googler and want to run this test, you need to provide a | 87 // If you're not a googler and want to run this test, you need to provide a |
76 // pesq binary for your platform (and sox.exe on windows). Read more on how | 88 // pesq binary for your platform (and sox.exe on windows). Read more on how |
77 // resources are managed in chrome/test/data/webrtc/resources/README. | 89 // resources are managed in chrome/test/data/webrtc/resources/README. |
78 // | 90 // |
79 // This test will only work on machines that have been configured to record | |
80 // their own input. | |
81 // | |
82 // On Linux: | 91 // On Linux: |
83 // 1. # sudo apt-get install pavucontrol sox | 92 // 1. # sudo apt-get install 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. | |
96 // | 93 // |
97 // On Mac: | 94 // On Mac: |
98 // TODO(phoglund): download sox from gs instead. | 95 // TODO(phoglund): download sox from gs instead. |
99 // 1. Get SoundFlower: http://rogueamoeba.com/freebies/soundflower/download.php | 96 // 1. Get SoundFlower: http://rogueamoeba.com/freebies/soundflower/download.php |
100 // 2. Install it + reboot. | 97 // 2. Install it + reboot. |
101 // 3. Install MacPorts (http://www.macports.org/). | 98 // 3. Install MacPorts (http://www.macports.org/). |
102 // 4. Install sox: sudo port install sox. | 99 // 4. Install sox: sudo port install sox. |
103 // 5. (For Chrome bots) Ensure sox and rec are reachable from the env the test | 100 // 5. (For Chrome bots) Ensure sox is reachable from the env the test |
104 // executes in (sox and rec tends to install in /opt/, which generally isn't | 101 // executes in (sox tends to install in /opt/, which generally isn't in the |
105 // in the Chrome bots' env). For instance, run | 102 // Chrome bots' env). For instance, run |
106 // sudo ln -s /opt/local/bin/rec /usr/local/bin/rec | |
107 // sudo ln -s /opt/local/bin/sox /usr/local/bin/sox | 103 // 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. | |
132 class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { | 104 class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { |
133 public: | 105 public: |
134 MAYBE_WebRtcAudioQualityBrowserTest() {} | 106 MAYBE_WebRtcAudioQualityBrowserTest() {} |
135 void SetUpInProcessBrowserTestFixture() override { | 107 void SetUpInProcessBrowserTestFixture() override { |
136 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. | 108 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. |
137 } | 109 } |
138 | 110 |
| 111 void SetUpOnMainThread() override { |
| 112 base::FilePath tmp_dir; |
| 113 EXPECT_TRUE(base::GetTempDir(&tmp_dir)); |
| 114 webm_recorded_output_filename_ = tmp_dir.Append(kWebmRecordingFilename); |
| 115 |
| 116 browser()->profile()->GetPrefs()->SetFilePath( |
| 117 prefs::kDownloadDefaultDirectory, tmp_dir); |
| 118 browser()->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload, |
| 119 false); |
| 120 } |
| 121 |
139 void SetUpCommandLine(base::CommandLine* command_line) override { | 122 void SetUpCommandLine(base::CommandLine* command_line) override { |
140 EXPECT_FALSE(command_line->HasSwitch( | 123 EXPECT_FALSE(command_line->HasSwitch( |
141 switches::kUseFakeUIForMediaStream)); | 124 switches::kUseFakeUIForMediaStream)); |
142 | 125 |
143 // The WebAudio-based tests don't care what devices are available to | 126 // The WebAudio-based tests don't care what devices are available to |
144 // getUserMedia, and the getUserMedia-based tests will play back a file | 127 // getUserMedia, and the getUserMedia-based tests will play back a file |
145 // through the fake device using using --use-file-for-fake-audio-capture. | 128 // through the fake device using using --use-file-for-fake-audio-capture. |
146 command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream); | 129 command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream); |
147 | 130 |
148 // Add loopback interface such that there is always connectivity. | 131 // Add loopback interface such that there is always connectivity. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 content::WebContents* tab_contents) { | 167 content::WebContents* tab_contents) { |
185 EXPECT_EQ("ok-muted", ExecuteJavascript( | 168 EXPECT_EQ("ok-muted", ExecuteJavascript( |
186 "setMediaElementMuted('" + element_id + "', true)", tab_contents)); | 169 "setMediaElementMuted('" + element_id + "', true)", tab_contents)); |
187 } | 170 } |
188 | 171 |
189 protected: | 172 protected: |
190 void TestAutoGainControl(const base::FilePath::StringType& reference_filename, | 173 void TestAutoGainControl(const base::FilePath::StringType& reference_filename, |
191 const std::string& constraints, | 174 const std::string& constraints, |
192 const std::string& perf_modifier); | 175 const std::string& perf_modifier); |
193 void SetupAndRecordAudioCall(const base::FilePath& reference_file, | 176 void SetupAndRecordAudioCall(const base::FilePath& reference_file, |
194 const base::FilePath& recording, | 177 const base::FilePath& recorded_output_path, |
195 const std::string& constraints, | 178 const std::string& constraints); |
196 const base::TimeDelta recording_time); | |
197 void TestWithFakeDeviceGetUserMedia(const std::string& constraints, | 179 void TestWithFakeDeviceGetUserMedia(const std::string& constraints, |
198 const std::string& perf_modifier); | 180 const std::string& perf_modifier); |
| 181 |
| 182 base::FilePath webm_recorded_output_filename_; |
199 }; | 183 }; |
200 | 184 |
201 namespace { | 185 namespace { |
202 | 186 |
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 | |
330 // Sox is the "Swiss army knife" of audio processing. We mainly use it for | 187 // Sox is the "Swiss army knife" of audio processing. We mainly use it for |
331 // silence trimming. See http://sox.sourceforge.net. | 188 // silence trimming. See http://sox.sourceforge.net. |
332 base::CommandLine MakeSoxCommandLine() { | 189 base::CommandLine MakeSoxCommandLine() { |
333 #if defined(OS_WIN) | 190 #if defined(OS_WIN) |
334 base::FilePath sox_path = test::GetToolForPlatform("sox"); | 191 base::FilePath sox_path = test::GetToolForPlatform("sox"); |
335 if (!base::PathExists(sox_path)) { | 192 if (!base::PathExists(sox_path)) { |
336 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() | 193 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() |
337 << "; you may have to provide this binary yourself."; | 194 << "; you may have to provide this binary yourself."; |
338 return base::CommandLine(base::CommandLine::NO_PROGRAM); | 195 return base::CommandLine(base::CommandLine::NO_PROGRAM); |
339 } | 196 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 command_line.AppendArg(kTreshold); | 237 command_line.AppendArg(kTreshold); |
381 command_line.AppendArg("reverse"); | 238 command_line.AppendArg("reverse"); |
382 | 239 |
383 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | 240 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
384 std::string result; | 241 std::string result; |
385 bool ok = base::GetAppOutput(command_line, &result); | 242 bool ok = base::GetAppOutput(command_line, &result); |
386 DVLOG(0) << "Output was:\n\n" << result; | 243 DVLOG(0) << "Output was:\n\n" << result; |
387 return ok; | 244 return ok; |
388 } | 245 } |
389 | 246 |
| 247 // Runs ffmpeg on the captured webm video and writes it to a .wav file. |
| 248 bool RunWebmToWavConverter(const base::FilePath& webm_recorded_output_path, |
| 249 const base::FilePath& wav_recorded_output_path) { |
| 250 const base::FilePath path_to_ffmpeg = test::GetToolForPlatform("ffmpeg"); |
| 251 if (!base::PathExists(path_to_ffmpeg)) { |
| 252 LOG(ERROR) << "Missing ffmpeg: should be in " << path_to_ffmpeg.value(); |
| 253 return false; |
| 254 } |
| 255 |
| 256 // Set up ffmpeg to output at a certain bitrate (-ab). This is hopefully set |
| 257 // high enough to avoid degrading audio quality too much. |
| 258 base::CommandLine ffmpeg_command(path_to_ffmpeg); |
| 259 ffmpeg_command.AppendArg("-i"); |
| 260 ffmpeg_command.AppendArgPath(webm_recorded_output_path); |
| 261 ffmpeg_command.AppendArg("-ab"); |
| 262 ffmpeg_command.AppendArg("300k"); |
| 263 ffmpeg_command.AppendArg("-y"); |
| 264 ffmpeg_command.AppendArgPath(wav_recorded_output_path); |
| 265 |
| 266 // We produce an output file that will later be used as an input to the |
| 267 // barcode decoder and frame analyzer tools. |
| 268 DVLOG(0) << "Running " << ffmpeg_command.GetCommandLineString(); |
| 269 std::string result; |
| 270 bool ok = base::GetAppOutputAndError(ffmpeg_command, &result); |
| 271 DVLOG(0) << "Output was:\n\n" << result; |
| 272 return ok; |
| 273 } |
| 274 |
390 // Looks for 0.2 second audio segments surrounded by silences under 0.3% audio | 275 // Looks for 0.2 second audio segments surrounded by silences under 0.3% audio |
391 // power and splits the input file on those silences. Output files are written | 276 // power and splits the input file on those silences. Output files are written |
392 // according to the output file template (e.g. /tmp/out.wav writes | 277 // according to the output file template (e.g. /tmp/out.wav writes |
393 // /tmp/out001.wav, /tmp/out002.wav, etc if there are two silence-padded | 278 // /tmp/out001.wav, /tmp/out002.wav, etc if there are two silence-padded |
394 // regions in the file). The silences between speech segments must be at | 279 // regions in the file). The silences between speech segments must be at |
395 // least 500 ms for this to be reliable. | 280 // least 500 ms for this to be reliable. |
396 bool SplitFileOnSilence(const base::FilePath& input_file, | 281 bool SplitFileOnSilence(const base::FilePath& input_file, |
397 const base::FilePath& output_file_template) { | 282 const base::FilePath& output_file_template) { |
398 base::CommandLine command_line = MakeSoxCommandLine(); | 283 base::CommandLine command_line = MakeSoxCommandLine(); |
399 if (command_line.GetProgram().empty()) | 284 if (command_line.GetProgram().empty()) |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
580 float difference_in_decibel = AnalyzeOneSegment(ref_segments[i], | 465 float difference_in_decibel = AnalyzeOneSegment(ref_segments[i], |
581 actual_segments[i], | 466 actual_segments[i], |
582 i); | 467 i); |
583 std::string trace_name = MakeTraceName(reference_file, i); | 468 std::string trace_name = MakeTraceName(reference_file, i); |
584 perf_test::PrintResult("agc_energy_diff", perf_modifier, trace_name, | 469 perf_test::PrintResult("agc_energy_diff", perf_modifier, trace_name, |
585 difference_in_decibel, "dB", false); | 470 difference_in_decibel, "dB", false); |
586 } | 471 } |
587 } | 472 } |
588 | 473 |
589 void ComputeAndPrintPesqResults(const base::FilePath& reference_file, | 474 void ComputeAndPrintPesqResults(const base::FilePath& reference_file, |
590 const base::FilePath& recording, | 475 const base::FilePath& recorded_output_path, |
591 const std::string& perf_modifier) { | 476 const std::string& perf_modifier) { |
592 base::FilePath trimmed_reference = CreateTemporaryWaveFile(); | 477 base::FilePath trimmed_reference = CreateTemporaryWaveFile(); |
593 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); | 478 base::FilePath trimmed_recording = CreateTemporaryWaveFile(); |
594 | 479 |
595 ASSERT_TRUE(RemoveSilence(reference_file, trimmed_reference)); | 480 ASSERT_TRUE(RemoveSilence(reference_file, trimmed_reference)); |
596 ASSERT_TRUE(RemoveSilence(recording, trimmed_recording)); | 481 ASSERT_TRUE(RemoveSilence(recorded_output_path, trimmed_recording)); |
597 | 482 |
598 std::string raw_mos; | 483 std::string raw_mos; |
599 std::string mos_lqo; | 484 std::string mos_lqo; |
600 bool succeeded = RunPesq(trimmed_reference, trimmed_recording, 16000, | 485 bool succeeded = RunPesq(trimmed_reference, trimmed_recording, 16000, |
601 &raw_mos, &mos_lqo); | 486 &raw_mos, &mos_lqo); |
602 EXPECT_TRUE(succeeded) << "Failed to run PESQ."; | 487 EXPECT_TRUE(succeeded) << "Failed to run PESQ."; |
603 if (succeeded) { | 488 if (succeeded) { |
604 perf_test::PrintResult( | 489 perf_test::PrintResult( |
605 "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); | 490 "audio_pesq", perf_modifier, "raw_mos", raw_mos, "score", true); |
606 perf_test::PrintResult( | 491 perf_test::PrintResult( |
607 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); | 492 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); |
608 } | 493 } |
609 | 494 |
610 DeleteFileUnlessTestFailed(trimmed_reference, false); | 495 DeleteFileUnlessTestFailed(trimmed_reference, false); |
611 DeleteFileUnlessTestFailed(trimmed_recording, false); | 496 DeleteFileUnlessTestFailed(trimmed_recording, false); |
612 } | 497 } |
613 | 498 |
614 } // namespace | 499 } // namespace |
615 | 500 |
616 // Sets up a two-way WebRTC call and records its output to |recording|, using | 501 // Sets up a two-way WebRTC call and records its output to |
617 // getUserMedia. | 502 // |recorded_output_path|, using getUserMedia. |
618 // | 503 // |
619 // |reference_file| should have at least five seconds of silence in the | 504 // |reference_file| should have at least five seconds of silence in the |
620 // beginning: otherwise all the reference audio will not be picked up by the | 505 // beginning: otherwise all the reference audio will not be picked up by the |
621 // recording. Note that the reference file will start playing as soon as the | 506 // recording. Note that the reference file will start playing as soon as the |
622 // audio device is up following the getUserMedia call in the left tab. The time | 507 // audio device is up following the getUserMedia call in the left tab. The time |
623 // it takes to negotiate a call isn't deterministic, but five seconds should be | 508 // it takes to negotiate a call isn't deterministic, but five seconds should be |
624 // plenty of time. Similarly, the recording time should be enough to catch the | 509 // plenty of time. Similarly, the recording time should be enough to catch the |
625 // whole reference file. If you then silence-trim the reference file and actual | 510 // whole reference file. If you then silence-trim the reference file and actual |
626 // file, you should end up with two time-synchronized files. | 511 // file, you should end up with two time-synchronized files. |
627 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( | 512 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( |
628 const base::FilePath& reference_file, | 513 const base::FilePath& reference_file, |
629 const base::FilePath& recording, | 514 const base::FilePath& recorded_output_path, |
630 const std::string& constraints, | 515 const std::string& constraints) { |
631 const base::TimeDelta recording_time) { | |
632 ASSERT_TRUE(embedded_test_server()->Start()); | 516 ASSERT_TRUE(embedded_test_server()->Start()); |
633 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 517 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
634 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); | |
635 | 518 |
636 ConfigureFakeDeviceToPlayFile(reference_file); | 519 ConfigureFakeDeviceToPlayFile(reference_file); |
637 | 520 |
638 // Create a two-way call. Mute one of the receivers though; that way it will | 521 // Create a two-way call. Mute one of the receivers though; that way it will |
639 // be receiving audio bytes, but we will not be playing out of both elements. | 522 // be receiving audio bytes, but we will not be playing out of both elements. |
640 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); | 523 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); |
641 content::WebContents* left_tab = | 524 content::WebContents* left_tab = |
642 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); | 525 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
643 SetupPeerconnectionWithLocalStream(left_tab); | 526 SetupPeerconnectionWithLocalStream(left_tab); |
644 MuteMediaElement("remote-view", left_tab); | 527 MuteMediaElement("remote-view", left_tab); |
645 | 528 |
646 content::WebContents* right_tab = | 529 content::WebContents* right_tab = |
647 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); | 530 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
648 SetupPeerconnectionWithLocalStream(right_tab); | 531 SetupPeerconnectionWithLocalStream(right_tab); |
649 | 532 |
650 AudioRecorder recorder; | |
651 ASSERT_TRUE(recorder.StartRecording(recording_time, recording)); | |
652 | |
653 NegotiateCall(left_tab, right_tab); | 533 NegotiateCall(left_tab, right_tab); |
654 | 534 |
655 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); | 535 EXPECT_EQ( |
656 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | 536 "ok-capturing", |
| 537 ExecuteJavascript( |
| 538 base::StringPrintf("startAudioCapture(%d, \"%s\");", |
| 539 kCaptureDurationInSeconds, kWebmRecordingFilename), |
| 540 right_tab)); |
| 541 |
| 542 EXPECT_TRUE(test::PollingWaitUntil("testIsDoneCapturing();", right_tab, |
| 543 kPollingIntervalInMs)); |
657 | 544 |
658 HangUp(left_tab); | 545 HangUp(left_tab); |
| 546 |
| 547 RunWebmToWavConverter(webm_recorded_output_filename_, recorded_output_path); |
| 548 EXPECT_TRUE(base::DieFileDie(webm_recorded_output_filename_, false)); |
| 549 |
| 550 DVLOG(0) << "Done recording to " << recorded_output_path.MaybeAsASCII(); |
659 } | 551 } |
660 | 552 |
661 void MAYBE_WebRtcAudioQualityBrowserTest::TestWithFakeDeviceGetUserMedia( | 553 void MAYBE_WebRtcAudioQualityBrowserTest::TestWithFakeDeviceGetUserMedia( |
662 const std::string& constraints, | 554 const std::string& constraints, |
663 const std::string& perf_modifier) { | 555 const std::string& perf_modifier) { |
664 if (OnWin8()) { | 556 if (OnWin8()) { |
665 // http://crbug.com/379798. | 557 // http://crbug.com/379798. |
666 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 558 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
667 return; | 559 return; |
668 } | 560 } |
669 | 561 |
670 base::FilePath reference_file = | 562 base::FilePath reference_file = |
671 test::GetReferenceFilesDir().Append(kReferenceFile); | 563 test::GetReferenceFilesDir().Append(kReferenceFile); |
672 base::FilePath recording = CreateTemporaryWaveFile(); | 564 base::FilePath recorded_output_path = CreateTemporaryWaveFile(); |
673 | 565 |
674 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( | 566 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( |
675 reference_file, recording, constraints, | 567 reference_file, recorded_output_path, constraints)); |
676 base::TimeDelta::FromSeconds(30))); | |
677 | 568 |
678 ComputeAndPrintPesqResults(reference_file, recording, perf_modifier); | 569 ComputeAndPrintPesqResults(reference_file, recorded_output_path, |
679 DeleteFileUnlessTestFailed(recording, false); | 570 perf_modifier); |
| 571 DeleteFileUnlessTestFailed(recorded_output_path, false); |
680 } | 572 } |
681 | 573 |
682 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 574 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
683 MANUAL_TestCallQualityWithAudioFromFakeDevice) { | 575 MANUAL_TestCallQualityWithAudioFromFakeDevice) { |
684 TestWithFakeDeviceGetUserMedia(kAudioOnlyCallConstraints, "_getusermedia"); | 576 TestWithFakeDeviceGetUserMedia(kAudioOnlyCallConstraints, "_getusermedia"); |
685 } | 577 } |
686 | 578 |
687 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 579 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
688 MANUAL_TestCallQualityWithAudioFromWebAudio) { | 580 MANUAL_TestCallQualityWithAudioFromWebAudio) { |
689 if (OnWin8()) { | 581 if (OnWin8()) { |
690 // http://crbug.com/379798. | 582 // http://crbug.com/379798. |
691 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 583 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
692 return; | 584 return; |
693 } | 585 } |
694 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 586 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
695 ASSERT_TRUE(embedded_test_server()->Start()); | 587 ASSERT_TRUE(embedded_test_server()->Start()); |
696 | 588 |
697 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); | |
698 | |
699 content::WebContents* left_tab = | 589 content::WebContents* left_tab = |
700 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 590 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
701 content::WebContents* right_tab = | 591 content::WebContents* right_tab = |
702 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 592 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
703 | 593 |
704 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); | 594 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); |
705 | 595 |
706 NegotiateCall(left_tab, right_tab); | 596 NegotiateCall(left_tab, right_tab); |
707 | 597 |
708 base::FilePath recording = CreateTemporaryWaveFile(); | 598 const base::FilePath recorded_output_path = 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)); | |
715 | 599 |
716 PlayAudioFileThroughWebAudio(left_tab); | 600 PlayAudioFileThroughWebAudio(left_tab); |
717 | 601 |
718 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); | 602 EXPECT_EQ( |
719 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | 603 "ok-capturing", |
| 604 ExecuteJavascript( |
| 605 base::StringPrintf("startAudioCapture(%d, \"%s\");", |
| 606 kCaptureDurationInSeconds, kWebmRecordingFilename), |
| 607 right_tab)); |
| 608 |
| 609 EXPECT_TRUE(test::PollingWaitUntil("testIsDoneCapturing();", right_tab, |
| 610 kPollingIntervalInMs)); |
720 | 611 |
721 HangUp(left_tab); | 612 HangUp(left_tab); |
722 | 613 |
| 614 RunWebmToWavConverter(webm_recorded_output_filename_, recorded_output_path); |
| 615 EXPECT_TRUE(base::DieFileDie(webm_recorded_output_filename_, false)); |
| 616 |
| 617 DVLOG(0) << "Done recording to " << recorded_output_path.MaybeAsASCII(); |
| 618 |
723 // Compare with the reference file on disk (this is the same file we played | 619 // Compare with the reference file on disk (this is the same file we played |
724 // through WebAudio earlier). | 620 // through WebAudio earlier). |
725 base::FilePath reference_file = | 621 base::FilePath reference_file = |
726 test::GetReferenceFilesDir().Append(kReferenceFile); | 622 test::GetReferenceFilesDir().Append(kReferenceFile); |
727 ComputeAndPrintPesqResults(reference_file, recording, "_webaudio"); | 623 ComputeAndPrintPesqResults(reference_file, recorded_output_path, "_webaudio"); |
728 } | 624 } |
729 | 625 |
730 /** | 626 /** |
731 * The auto gain control test plays a file into the fake microphone. Then it | 627 * The auto gain control test plays a file into the fake microphone. Then it |
732 * sets up a one-way WebRTC call with audio only and records Chrome's output on | 628 * sets up a one-way WebRTC call with audio only and records Chrome's output on |
733 * the receiving side using the audio loopback provided by the quality test | 629 * the receiving side using the audio loopback provided by the quality test |
734 * (see the class comments for more details). | 630 * (see the class comments for more details). |
735 * | 631 * |
736 * Then both the recording and reference file are split on silence. This creates | 632 * Then both the recording and reference file are split on silence. This creates |
737 * a number of segments with speech in them. The reason for this is to provide | 633 * a number of segments with speech in them. The reason for this is to provide |
(...skipping 23 matching lines...) Expand all Loading... |
761 const base::FilePath::StringType& reference_filename, | 657 const base::FilePath::StringType& reference_filename, |
762 const std::string& constraints, | 658 const std::string& constraints, |
763 const std::string& perf_modifier) { | 659 const std::string& perf_modifier) { |
764 if (OnWin8()) { | 660 if (OnWin8()) { |
765 // http://crbug.com/379798. | 661 // http://crbug.com/379798. |
766 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 662 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
767 return; | 663 return; |
768 } | 664 } |
769 base::FilePath reference_file = | 665 base::FilePath reference_file = |
770 test::GetReferenceFilesDir().Append(reference_filename); | 666 test::GetReferenceFilesDir().Append(reference_filename); |
771 base::FilePath recording = CreateTemporaryWaveFile(); | 667 base::FilePath recorded_output_path = CreateTemporaryWaveFile(); |
772 | 668 |
773 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( | 669 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( |
774 reference_file, recording, constraints, | 670 reference_file, recorded_output_path, constraints)); |
775 base::TimeDelta::FromSeconds(30))); | |
776 | 671 |
777 base::ScopedTempDir split_ref_files; | 672 base::ScopedTempDir split_ref_files; |
778 ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); | 673 ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); |
779 ASSERT_NO_FATAL_FAILURE( | 674 ASSERT_NO_FATAL_FAILURE( |
780 SplitFileOnSilenceIntoDir(reference_file, split_ref_files.GetPath())); | 675 SplitFileOnSilenceIntoDir(reference_file, split_ref_files.GetPath())); |
781 std::vector<base::FilePath> ref_segments = | 676 std::vector<base::FilePath> ref_segments = |
782 ListWavFilesInDir(split_ref_files.GetPath()); | 677 ListWavFilesInDir(split_ref_files.GetPath()); |
783 | 678 |
784 base::ScopedTempDir split_actual_files; | 679 base::ScopedTempDir split_actual_files; |
785 ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); | 680 ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); |
786 ASSERT_NO_FATAL_FAILURE( | 681 ASSERT_NO_FATAL_FAILURE(SplitFileOnSilenceIntoDir( |
787 SplitFileOnSilenceIntoDir(recording, split_actual_files.GetPath())); | 682 recorded_output_path, split_actual_files.GetPath())); |
788 | 683 |
789 // Keep the recording and split files if the analysis fails. | 684 // Keep the recording and split files if the analysis fails. |
790 base::FilePath actual_files_dir = split_actual_files.Take(); | 685 base::FilePath actual_files_dir = split_actual_files.Take(); |
791 std::vector<base::FilePath> actual_segments = | 686 std::vector<base::FilePath> actual_segments = |
792 ListWavFilesInDir(actual_files_dir); | 687 ListWavFilesInDir(actual_files_dir); |
793 | 688 |
794 AnalyzeSegmentsAndPrintResult( | 689 AnalyzeSegmentsAndPrintResult( |
795 ref_segments, actual_segments, reference_file, perf_modifier); | 690 ref_segments, actual_segments, reference_file, perf_modifier); |
796 | 691 |
797 DeleteFileUnlessTestFailed(recording, false); | 692 DeleteFileUnlessTestFailed(recorded_output_path, false); |
798 DeleteFileUnlessTestFailed(actual_files_dir, true); | 693 DeleteFileUnlessTestFailed(actual_files_dir, true); |
799 } | 694 } |
800 | 695 |
801 // The AGC should apply non-zero gain here. | 696 // The AGC should apply non-zero gain here. |
802 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 697 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
803 MANUAL_TestAutoGainControlOnLowAudio) { | 698 MANUAL_TestAutoGainControlOnLowAudio) { |
804 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | 699 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
805 kReferenceFile, kAudioOnlyCallConstraints, "_with_agc")); | 700 kReferenceFile, kAudioOnlyCallConstraints, "_with_agc")); |
806 } | 701 } |
807 | 702 |
808 // Since the AGC is off here there should be no gain at all. | 703 // Since the AGC is off here there should be no gain at all. |
809 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 704 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
810 MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { | 705 MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { |
811 const char* kAudioCallWithoutAudioProcessing = | 706 const char* kAudioCallWithoutAudioProcessing = |
812 "{audio: { mandatory: { echoCancellation: false } } }"; | 707 "{audio: { mandatory: { echoCancellation: false } } }"; |
813 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | 708 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
814 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); | 709 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); |
815 } | 710 } |
OLD | NEW |