Chromium Code Reviews| 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/base64.h" | |
| 9 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 10 #include "base/files/file_enumerator.h" | 11 #include "base/files/file_enumerator.h" |
| 11 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
| 12 #include "base/files/scoped_temp_dir.h" | 13 #include "base/files/scoped_temp_dir.h" |
| 13 #include "base/macros.h" | 14 #include "base/macros.h" |
| 14 #include "base/process/launch.h" | 15 #include "base/process/launch.h" |
| 15 #include "base/process/process.h" | 16 #include "base/process/process.h" |
| 16 #include "base/scoped_native_library.h" | 17 #include "base/scoped_native_library.h" |
| 17 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 43 | 44 |
| 44 // The javascript will load the reference file relative to its location, | 45 // 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 | 46 // 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. | 47 // webrtc/resources in the chrome/test/data folder. |
| 47 static const char kReferenceFileRelativeUrl[] = | 48 static const char kReferenceFileRelativeUrl[] = |
| 48 "resources/speech_44kHz_16bit_stereo.wav"; | 49 "resources/speech_44kHz_16bit_stereo.wav"; |
| 49 | 50 |
| 50 static const char kWebRtcAudioTestHtmlPage[] = | 51 static const char kWebRtcAudioTestHtmlPage[] = |
| 51 "/webrtc/webrtc_audio_quality_test.html"; | 52 "/webrtc/webrtc_audio_quality_test.html"; |
| 52 | 53 |
| 54 // How often to ask the test page whether the audio recording is completed. | |
| 55 const int kPollingIntervalInMs = 1000; | |
| 56 | |
| 53 // For the AGC test, there are 6 speech segments split on silence. If one | 57 // 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 | 58 // segment is significantly different in length compared to the same segment in |
| 55 // the reference file, there's something fishy going on. | 59 // the reference file, there's something fishy going on. |
| 56 const int kMaxAgcSegmentDiffMs = | 60 const int kMaxAgcSegmentDiffMs = |
| 57 #if defined(OS_MACOSX) | 61 #if defined(OS_MACOSX) |
| 58 // Something is different on Mac; http://crbug.com/477653. | 62 // Something is different on Mac; http://crbug.com/477653. |
| 59 600; | 63 600; |
| 60 #else | 64 #else |
| 61 200; | 65 200; |
| 62 #endif | 66 #endif |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 73 // Test we can set up a WebRTC call and play audio through it. | 77 // Test we can set up a WebRTC call and play audio through it. |
| 74 // | 78 // |
| 75 // If you're not a googler and want to run this test, you need to provide a | 79 // 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 | 80 // 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. | 81 // resources are managed in chrome/test/data/webrtc/resources/README. |
| 78 // | 82 // |
| 79 // This test will only work on machines that have been configured to record | 83 // This test will only work on machines that have been configured to record |
| 80 // their own input. | 84 // their own input. |
| 81 // | 85 // |
| 82 // On Linux: | 86 // On Linux: |
| 83 // 1. # sudo apt-get install pavucontrol sox | 87 // 1. # sudo apt-get install sox |
| 84 // 2. For the user who will run the test: # pavucontrol | 88 // 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 // | 89 // |
| 97 // On Mac: | 90 // On Mac: |
| 98 // TODO(phoglund): download sox from gs instead. | 91 // TODO(phoglund): download sox from gs instead. |
| 99 // 1. Get SoundFlower: http://rogueamoeba.com/freebies/soundflower/download.php | 92 // 1. Get SoundFlower: http://rogueamoeba.com/freebies/soundflower/download.php |
| 100 // 2. Install it + reboot. | 93 // 2. Install it + reboot. |
| 101 // 3. Install MacPorts (http://www.macports.org/). | 94 // 3. Install MacPorts (http://www.macports.org/). |
| 102 // 4. Install sox: sudo port install sox. | 95 // 4. Install sox: sudo port install sox. |
| 103 // 5. (For Chrome bots) Ensure sox and rec are reachable from the env the test | 96 // 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 | 97 // executes in (sox and rec tends to install in /opt/, which generally isn't |
| 105 // in the Chrome bots' env). For instance, run | 98 // in the Chrome bots' env). For instance, run |
| 106 // sudo ln -s /opt/local/bin/rec /usr/local/bin/rec | 99 // sudo ln -s /opt/local/bin/rec /usr/local/bin/rec |
|
kjellander_chromium
2017/03/21 14:07:26
Remove this line too :)
| |
| 107 // sudo ln -s /opt/local/bin/sox /usr/local/bin/sox | 100 // 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 { | 101 class MAYBE_WebRtcAudioQualityBrowserTest : public WebRtcTestBase { |
| 133 public: | 102 public: |
| 134 MAYBE_WebRtcAudioQualityBrowserTest() {} | 103 MAYBE_WebRtcAudioQualityBrowserTest() {} |
| 135 void SetUpInProcessBrowserTestFixture() override { | 104 void SetUpInProcessBrowserTestFixture() override { |
| 136 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. | 105 DetectErrorsInJavaScript(); // Look for errors in our rather complex js. |
| 137 } | 106 } |
| 138 | 107 |
| 139 void SetUpCommandLine(base::CommandLine* command_line) override { | 108 void SetUpCommandLine(base::CommandLine* command_line) override { |
| 140 EXPECT_FALSE(command_line->HasSwitch( | 109 EXPECT_FALSE(command_line->HasSwitch( |
| 141 switches::kUseFakeUIForMediaStream)); | 110 switches::kUseFakeUIForMediaStream)); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 ExecuteJavascript("preparePeerConnection()", tab)); | 148 ExecuteJavascript("preparePeerConnection()", tab)); |
| 180 return tab; | 149 return tab; |
| 181 } | 150 } |
| 182 | 151 |
| 183 void MuteMediaElement(const std::string& element_id, | 152 void MuteMediaElement(const std::string& element_id, |
| 184 content::WebContents* tab_contents) { | 153 content::WebContents* tab_contents) { |
| 185 EXPECT_EQ("ok-muted", ExecuteJavascript( | 154 EXPECT_EQ("ok-muted", ExecuteJavascript( |
| 186 "setMediaElementMuted('" + element_id + "', true)", tab_contents)); | 155 "setMediaElementMuted('" + element_id + "', true)", tab_contents)); |
| 187 } | 156 } |
| 188 | 157 |
| 158 void WriteCapturedAudio(content::WebContents* capturing_tab, | |
| 159 const base::FilePath& audio_filename); | |
| 160 | |
| 189 protected: | 161 protected: |
| 190 void TestAutoGainControl(const base::FilePath::StringType& reference_filename, | 162 void TestAutoGainControl(const base::FilePath::StringType& reference_filename, |
| 191 const std::string& constraints, | 163 const std::string& constraints, |
| 192 const std::string& perf_modifier); | 164 const std::string& perf_modifier); |
| 193 void SetupAndRecordAudioCall(const base::FilePath& reference_file, | 165 void SetupAndRecordAudioCall(const base::FilePath& reference_file, |
| 194 const base::FilePath& recording, | 166 const base::FilePath& recording, |
| 195 const std::string& constraints, | 167 const std::string& constraints); |
| 196 const base::TimeDelta recording_time); | |
| 197 void TestWithFakeDeviceGetUserMedia(const std::string& constraints, | 168 void TestWithFakeDeviceGetUserMedia(const std::string& constraints, |
| 198 const std::string& perf_modifier); | 169 const std::string& perf_modifier); |
| 199 }; | 170 }; |
| 200 | 171 |
| 201 namespace { | 172 namespace { |
| 202 | 173 |
| 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 | 174 // Sox is the "Swiss army knife" of audio processing. We mainly use it for |
| 331 // silence trimming. See http://sox.sourceforge.net. | 175 // silence trimming. See http://sox.sourceforge.net. |
| 332 base::CommandLine MakeSoxCommandLine() { | 176 base::CommandLine MakeSoxCommandLine() { |
| 333 #if defined(OS_WIN) | 177 #if defined(OS_WIN) |
| 334 base::FilePath sox_path = test::GetToolForPlatform("sox"); | 178 base::FilePath sox_path = test::GetToolForPlatform("sox"); |
| 335 if (!base::PathExists(sox_path)) { | 179 if (!base::PathExists(sox_path)) { |
| 336 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() | 180 LOG(ERROR) << "Missing sox.exe binary in " << sox_path.value() |
| 337 << "; you may have to provide this binary yourself."; | 181 << "; you may have to provide this binary yourself."; |
| 338 return base::CommandLine(base::CommandLine::NO_PROGRAM); | 182 return base::CommandLine(base::CommandLine::NO_PROGRAM); |
| 339 } | 183 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 380 command_line.AppendArg(kTreshold); | 224 command_line.AppendArg(kTreshold); |
| 381 command_line.AppendArg("reverse"); | 225 command_line.AppendArg("reverse"); |
| 382 | 226 |
| 383 DVLOG(0) << "Running " << command_line.GetCommandLineString(); | 227 DVLOG(0) << "Running " << command_line.GetCommandLineString(); |
| 384 std::string result; | 228 std::string result; |
| 385 bool ok = base::GetAppOutput(command_line, &result); | 229 bool ok = base::GetAppOutput(command_line, &result); |
| 386 DVLOG(0) << "Output was:\n\n" << result; | 230 DVLOG(0) << "Output was:\n\n" << result; |
| 387 return ok; | 231 return ok; |
| 388 } | 232 } |
| 389 | 233 |
| 234 // Runs ffmpeg on the captured webm video and writes it to a .wav file. | |
| 235 bool RunWebmToWavConverter(const base::FilePath& webm_audio_filename, | |
| 236 const base::FilePath& wav_audio_filename) { | |
| 237 base::FilePath path_to_ffmpeg = test::GetToolForPlatform("ffmpeg"); | |
| 238 if (!base::PathExists(path_to_ffmpeg)) { | |
| 239 LOG(ERROR) << "Missing ffmpeg: should be in " << path_to_ffmpeg.value(); | |
| 240 return false; | |
| 241 } | |
| 242 | |
| 243 // Set up ffmpeg to output at a certain bitrate (-ab). This is hopefully set | |
| 244 // high enough to avoid degrading audio quality too much. | |
| 245 base::CommandLine ffmpeg_command(path_to_ffmpeg); | |
| 246 ffmpeg_command.AppendArg("-i"); | |
| 247 ffmpeg_command.AppendArgPath(webm_audio_filename); | |
| 248 ffmpeg_command.AppendArg("-ab"); | |
| 249 ffmpeg_command.AppendArg("300k"); | |
| 250 ffmpeg_command.AppendArg("-y"); | |
| 251 ffmpeg_command.AppendArgPath(wav_audio_filename); | |
| 252 | |
| 253 // We produce an output file that will later be used as an input to the | |
| 254 // barcode decoder and frame analyzer tools. | |
| 255 DVLOG(0) << "Running " << ffmpeg_command.GetCommandLineString(); | |
| 256 std::string result; | |
| 257 bool ok = base::GetAppOutputAndError(ffmpeg_command, &result); | |
| 258 DVLOG(0) << "Output was:\n\n" << result; | |
| 259 return ok; | |
| 260 } | |
| 261 | |
| 390 // Looks for 0.2 second audio segments surrounded by silences under 0.3% audio | 262 // 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 | 263 // 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 | 264 // 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 | 265 // /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 | 266 // regions in the file). The silences between speech segments must be at |
| 395 // least 500 ms for this to be reliable. | 267 // least 500 ms for this to be reliable. |
| 396 bool SplitFileOnSilence(const base::FilePath& input_file, | 268 bool SplitFileOnSilence(const base::FilePath& input_file, |
| 397 const base::FilePath& output_file_template) { | 269 const base::FilePath& output_file_template) { |
| 398 base::CommandLine command_line = MakeSoxCommandLine(); | 270 base::CommandLine command_line = MakeSoxCommandLine(); |
| 399 if (command_line.GetProgram().empty()) | 271 if (command_line.GetProgram().empty()) |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 606 perf_test::PrintResult( | 478 perf_test::PrintResult( |
| 607 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); | 479 "audio_pesq", perf_modifier, "mos_lqo", mos_lqo, "score", true); |
| 608 } | 480 } |
| 609 | 481 |
| 610 DeleteFileUnlessTestFailed(trimmed_reference, false); | 482 DeleteFileUnlessTestFailed(trimmed_reference, false); |
| 611 DeleteFileUnlessTestFailed(trimmed_recording, false); | 483 DeleteFileUnlessTestFailed(trimmed_recording, false); |
| 612 } | 484 } |
| 613 | 485 |
| 614 } // namespace | 486 } // namespace |
| 615 | 487 |
| 488 void MAYBE_WebRtcAudioQualityBrowserTest::WriteCapturedAudio( | |
| 489 content::WebContents* capturing_tab, | |
| 490 const base::FilePath& audio_filename) { | |
| 491 base::FilePath audio_filename_webm = | |
| 492 audio_filename.AddExtension(FILE_PATH_LITERAL(".webm")); | |
| 493 | |
| 494 std::string base64_encoded_audio = | |
| 495 ExecuteJavascript("getRecordedAudioAsBase64()", capturing_tab); | |
| 496 std::string recorded_audio; | |
| 497 ASSERT_TRUE(base::Base64Decode(base64_encoded_audio, &recorded_audio)); | |
| 498 base::File audio_file(audio_filename_webm, | |
| 499 base::File::FLAG_CREATE | base::File::FLAG_WRITE); | |
| 500 size_t written = | |
| 501 audio_file.Write(0, recorded_audio.c_str(), recorded_audio.length()); | |
| 502 ASSERT_EQ(recorded_audio.length(), written); | |
| 503 | |
| 504 RunWebmToWavConverter(audio_filename_webm, audio_filename); | |
| 505 } | |
| 506 | |
| 616 // Sets up a two-way WebRTC call and records its output to |recording|, using | 507 // Sets up a two-way WebRTC call and records its output to |recording|, using |
| 617 // getUserMedia. | 508 // getUserMedia. |
| 618 // | 509 // |
| 619 // |reference_file| should have at least five seconds of silence in the | 510 // |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 | 511 // 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 | 512 // 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 | 513 // 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 | 514 // 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 | 515 // 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 | 516 // whole reference file. If you then silence-trim the reference file and actual |
| 626 // file, you should end up with two time-synchronized files. | 517 // file, you should end up with two time-synchronized files. |
| 627 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( | 518 void MAYBE_WebRtcAudioQualityBrowserTest::SetupAndRecordAudioCall( |
| 628 const base::FilePath& reference_file, | 519 const base::FilePath& reference_file, |
| 629 const base::FilePath& recording, | 520 const base::FilePath& recording, |
| 630 const std::string& constraints, | 521 const std::string& constraints) { |
| 631 const base::TimeDelta recording_time) { | |
| 632 ASSERT_TRUE(embedded_test_server()->Start()); | 522 ASSERT_TRUE(embedded_test_server()->Start()); |
| 633 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 523 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
| 634 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); | |
| 635 | 524 |
| 636 ConfigureFakeDeviceToPlayFile(reference_file); | 525 ConfigureFakeDeviceToPlayFile(reference_file); |
| 637 | 526 |
| 638 // Create a two-way call. Mute one of the receivers though; that way it will | 527 // 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. | 528 // be receiving audio bytes, but we will not be playing out of both elements. |
| 640 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); | 529 GURL test_page = embedded_test_server()->GetURL(kWebRtcAudioTestHtmlPage); |
| 641 content::WebContents* left_tab = | 530 content::WebContents* left_tab = |
| 642 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); | 531 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
| 643 SetupPeerconnectionWithLocalStream(left_tab); | 532 SetupPeerconnectionWithLocalStream(left_tab); |
| 644 MuteMediaElement("remote-view", left_tab); | 533 MuteMediaElement("remote-view", left_tab); |
| 645 | 534 |
| 646 content::WebContents* right_tab = | 535 content::WebContents* right_tab = |
| 647 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); | 536 OpenPageAndGetUserMediaInNewTabWithConstraints(test_page, constraints); |
| 648 SetupPeerconnectionWithLocalStream(right_tab); | 537 SetupPeerconnectionWithLocalStream(right_tab); |
| 649 | 538 |
| 650 AudioRecorder recorder; | |
| 651 ASSERT_TRUE(recorder.StartRecording(recording_time, recording)); | |
| 652 | |
| 653 NegotiateCall(left_tab, right_tab); | 539 NegotiateCall(left_tab, right_tab); |
| 654 | 540 |
| 655 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); | 541 EXPECT_TRUE(test::PollingWaitUntil("doneCapturingAudio()", "done-capturing", |
| 656 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | 542 right_tab, kPollingIntervalInMs)); |
| 657 | 543 |
| 658 HangUp(left_tab); | 544 HangUp(left_tab); |
| 545 | |
| 546 WriteCapturedAudio(right_tab, recording); | |
| 547 | |
| 548 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | |
| 659 } | 549 } |
| 660 | 550 |
| 661 void MAYBE_WebRtcAudioQualityBrowserTest::TestWithFakeDeviceGetUserMedia( | 551 void MAYBE_WebRtcAudioQualityBrowserTest::TestWithFakeDeviceGetUserMedia( |
| 662 const std::string& constraints, | 552 const std::string& constraints, |
| 663 const std::string& perf_modifier) { | 553 const std::string& perf_modifier) { |
| 664 if (OnWin8()) { | 554 if (OnWin8()) { |
| 665 // http://crbug.com/379798. | 555 // http://crbug.com/379798. |
| 666 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 556 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
| 667 return; | 557 return; |
| 668 } | 558 } |
| 669 | 559 |
| 670 base::FilePath reference_file = | 560 base::FilePath reference_file = |
| 671 test::GetReferenceFilesDir().Append(kReferenceFile); | 561 test::GetReferenceFilesDir().Append(kReferenceFile); |
| 672 base::FilePath recording = CreateTemporaryWaveFile(); | 562 base::FilePath recording = CreateTemporaryWaveFile(); |
| 673 | 563 |
| 674 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( | 564 ASSERT_NO_FATAL_FAILURE( |
| 675 reference_file, recording, constraints, | 565 SetupAndRecordAudioCall(reference_file, recording, constraints)); |
| 676 base::TimeDelta::FromSeconds(30))); | |
| 677 | 566 |
| 678 ComputeAndPrintPesqResults(reference_file, recording, perf_modifier); | 567 ComputeAndPrintPesqResults(reference_file, recording, perf_modifier); |
| 679 DeleteFileUnlessTestFailed(recording, false); | 568 DeleteFileUnlessTestFailed(recording, false); |
| 680 } | 569 } |
| 681 | 570 |
| 682 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 571 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
| 683 MANUAL_TestCallQualityWithAudioFromFakeDevice) { | 572 MANUAL_TestCallQualityWithAudioFromFakeDevice) { |
| 684 TestWithFakeDeviceGetUserMedia(kAudioOnlyCallConstraints, "_getusermedia"); | 573 TestWithFakeDeviceGetUserMedia(kAudioOnlyCallConstraints, "_getusermedia"); |
| 685 } | 574 } |
| 686 | 575 |
| 687 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 576 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
| 688 MANUAL_TestCallQualityWithAudioFromWebAudio) { | 577 MANUAL_TestCallQualityWithAudioFromWebAudio) { |
| 689 if (OnWin8()) { | 578 if (OnWin8()) { |
| 690 // http://crbug.com/379798. | 579 // http://crbug.com/379798. |
| 691 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 580 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
| 692 return; | 581 return; |
| 693 } | 582 } |
| 694 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); | 583 ASSERT_TRUE(test::HasReferenceFilesInCheckout()); |
| 695 ASSERT_TRUE(embedded_test_server()->Start()); | 584 ASSERT_TRUE(embedded_test_server()->Start()); |
| 696 | 585 |
| 697 ASSERT_TRUE(ForceMicrophoneVolumeTo100Percent()); | |
| 698 | |
| 699 content::WebContents* left_tab = | 586 content::WebContents* left_tab = |
| 700 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 587 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
| 701 content::WebContents* right_tab = | 588 content::WebContents* right_tab = |
| 702 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); | 589 OpenPageWithoutGetUserMedia(kWebRtcAudioTestHtmlPage); |
| 703 | 590 |
| 704 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); | 591 AddAudioFileToWebAudio(kReferenceFileRelativeUrl, left_tab); |
| 705 | 592 |
| 706 NegotiateCall(left_tab, right_tab); | 593 NegotiateCall(left_tab, right_tab); |
| 707 | 594 |
| 708 base::FilePath recording = CreateTemporaryWaveFile(); | 595 base::FilePath recording = CreateTemporaryWaveFile(); |
| 709 | 596 |
| 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 | |
| 716 PlayAudioFileThroughWebAudio(left_tab); | 597 PlayAudioFileThroughWebAudio(left_tab); |
| 717 | 598 |
| 718 ASSERT_TRUE(recorder.WaitForRecordingToEnd()); | 599 EXPECT_TRUE(test::PollingWaitUntil("doneCapturingAudio()", "done-capturing", |
| 719 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | 600 right_tab, kPollingIntervalInMs)); |
| 720 | 601 |
| 721 HangUp(left_tab); | 602 HangUp(left_tab); |
| 722 | 603 |
| 604 WriteCapturedAudio(right_tab, recording); | |
| 605 | |
| 606 DVLOG(0) << "Done recording to " << recording.value() << std::endl; | |
| 607 | |
| 723 // Compare with the reference file on disk (this is the same file we played | 608 // Compare with the reference file on disk (this is the same file we played |
| 724 // through WebAudio earlier). | 609 // through WebAudio earlier). |
| 725 base::FilePath reference_file = | 610 base::FilePath reference_file = |
| 726 test::GetReferenceFilesDir().Append(kReferenceFile); | 611 test::GetReferenceFilesDir().Append(kReferenceFile); |
| 727 ComputeAndPrintPesqResults(reference_file, recording, "_webaudio"); | 612 ComputeAndPrintPesqResults(reference_file, recording, "_webaudio"); |
| 728 } | 613 } |
| 729 | 614 |
| 730 /** | 615 /** |
| 731 * The auto gain control test plays a file into the fake microphone. Then it | 616 * 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 | 617 * sets up a one-way WebRTC call with audio only and records Chrome's output on |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 763 const std::string& perf_modifier) { | 648 const std::string& perf_modifier) { |
| 764 if (OnWin8()) { | 649 if (OnWin8()) { |
| 765 // http://crbug.com/379798. | 650 // http://crbug.com/379798. |
| 766 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; | 651 LOG(ERROR) << "This test is not implemented for Windows XP/Win8."; |
| 767 return; | 652 return; |
| 768 } | 653 } |
| 769 base::FilePath reference_file = | 654 base::FilePath reference_file = |
| 770 test::GetReferenceFilesDir().Append(reference_filename); | 655 test::GetReferenceFilesDir().Append(reference_filename); |
| 771 base::FilePath recording = CreateTemporaryWaveFile(); | 656 base::FilePath recording = CreateTemporaryWaveFile(); |
| 772 | 657 |
| 773 ASSERT_NO_FATAL_FAILURE(SetupAndRecordAudioCall( | 658 ASSERT_NO_FATAL_FAILURE( |
| 774 reference_file, recording, constraints, | 659 SetupAndRecordAudioCall(reference_file, recording, constraints)); |
| 775 base::TimeDelta::FromSeconds(30))); | |
| 776 | 660 |
| 777 base::ScopedTempDir split_ref_files; | 661 base::ScopedTempDir split_ref_files; |
| 778 ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); | 662 ASSERT_TRUE(split_ref_files.CreateUniqueTempDir()); |
| 779 ASSERT_NO_FATAL_FAILURE( | 663 ASSERT_NO_FATAL_FAILURE( |
| 780 SplitFileOnSilenceIntoDir(reference_file, split_ref_files.GetPath())); | 664 SplitFileOnSilenceIntoDir(reference_file, split_ref_files.GetPath())); |
| 781 std::vector<base::FilePath> ref_segments = | 665 std::vector<base::FilePath> ref_segments = |
| 782 ListWavFilesInDir(split_ref_files.GetPath()); | 666 ListWavFilesInDir(split_ref_files.GetPath()); |
| 783 | 667 |
| 784 base::ScopedTempDir split_actual_files; | 668 base::ScopedTempDir split_actual_files; |
| 785 ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); | 669 ASSERT_TRUE(split_actual_files.CreateUniqueTempDir()); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 806 } | 690 } |
| 807 | 691 |
| 808 // Since the AGC is off here there should be no gain at all. | 692 // Since the AGC is off here there should be no gain at all. |
| 809 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, | 693 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioQualityBrowserTest, |
| 810 MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { | 694 MANUAL_TestAutoGainIsOffWithAudioProcessingOff) { |
| 811 const char* kAudioCallWithoutAudioProcessing = | 695 const char* kAudioCallWithoutAudioProcessing = |
| 812 "{audio: { mandatory: { echoCancellation: false } } }"; | 696 "{audio: { mandatory: { echoCancellation: false } } }"; |
| 813 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( | 697 ASSERT_NO_FATAL_FAILURE(TestAutoGainControl( |
| 814 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); | 698 kReferenceFile, kAudioCallWithoutAudioProcessing, "_no_agc")); |
| 815 } | 699 } |
| OLD | NEW |