chrome_webrtc_video_quality_browsertest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
10ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// Copyright 2013 The Chromium Authors. All rights reserved. 21d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling// Use of this source code is governed by a BSD-style license that can be 30ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// found in the LICENSE file. 40ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong 50ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/environment.h" 60ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/file_util.h" 70ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/path_service.h" 80ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/process/launch.h" 90ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/strings/string_split.h" 100ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/strings/stringprintf.h" 110ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/test/test_timeouts.h" 120ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "base/time/time.h" 130ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/chrome_notification_types.h" 140ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/infobars/infobar.h" 150ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/infobars/infobar_service.h" 160ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/media/media_stream_infobar_delegate.h" 170ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/media/webrtc_browsertest_base.h" 180ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/media/webrtc_browsertest_common.h" 190ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/profiles/profile.h" 200ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/ui/browser.h" 210ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/ui/browser_tabstrip.h" 220ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/browser/ui/tabs/tab_strip_model.h" 230ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/common/chrome_switches.h" 240ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/test/base/in_process_browser_test.h" 250ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/test/base/ui_test_utils.h" 260ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "chrome/test/ui/ui_test.h" 270ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "content/public/browser/notification_service.h" 280ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "content/public/test/browser_test_utils.h" 290ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "net/test/embedded_test_server/embedded_test_server.h" 300ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "net/test/python_utils.h" 310ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#include "testing/perf/perf_test.h" 320ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong 330ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongstatic const base::FilePath::CharType kFrameAnalyzerExecutable[] = 340ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#if defined(OS_WIN) 350ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong FILE_PATH_LITERAL("frame_analyzer.exe"); 360ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#else 370ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong FILE_PATH_LITERAL("frame_analyzer"); 380ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#endif 390ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong 4079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandezstatic const base::FilePath::CharType kArgbToI420ConverterExecutable[] = 410ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#if defined(OS_WIN) 420ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong FILE_PATH_LITERAL("rgba_to_i420_converter.exe"); 4379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#else 4479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez FILE_PATH_LITERAL("rgba_to_i420_converter"); 4579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#endif 461d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberling 471d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberlingstatic const char kHomeEnvName[] = 480ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#if defined(OS_WIN) 490ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong "HOMEPATH"; 500ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#else 510ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong "HOME"; 520ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong#endif 530ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong 540ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong// The working dir should be in the user's home folder. 550ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongstatic const base::FilePath::CharType kWorkingDirName[] = 560ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong FILE_PATH_LITERAL("webrtc_video_quality"); 570ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kongstatic const base::FilePath::CharType kReferenceYuvFileName[] = 580ae28bd5885b5daa526898fcf7c323dc2c3e1963Angus Kong FILE_PATH_LITERAL("reference_video.yuv"); 591d2624a10e2c559f8ba9ef89eaa30832c0a83a96Sascha Haeberlingstatic const base::FilePath::CharType kCapturedYuvFileName[] = 60 FILE_PATH_LITERAL("captured_video.yuv"); 61static const base::FilePath::CharType kStatsFileName[] = 62 FILE_PATH_LITERAL("stats.txt"); 63static const char kMainWebrtcTestHtmlPage[] = 64 "/webrtc/webrtc_jsep01_test.html"; 65static const char kCapturingWebrtcHtmlPage[] = 66 "/webrtc/webrtc_video_quality_test.html"; 67static const int kVgaWidth = 640; 68static const int kVgaHeight = 480; 69 70// If you change the port number, don't forget to modify video_extraction.js 71// too! 72static const char kPyWebSocketPortNumber[] = "12221"; 73 74// Test the video quality of the WebRTC output. 75// 76// Prerequisites: This test case must run on a machine with a virtual webcam 77// that plays video from the reference file located in <the running user's home 78// folder>/kWorkingDirName/kReferenceYuvFileName. 79// 80// You must also compile the chromium_builder_webrtc target before you run this 81// test to get all the tools built. 82// 83// The external compare_videos.py script also depends on two external 84// executables which must be located in the PATH when running this test. 85// * zxing (see the CPP version at https://code.google.com/p/zxing) 86// * ffmpeg 0.11.1 or compatible version (see http://www.ffmpeg.org) 87// 88// The test case will launch a custom binary (peerconnection_server) which will 89// allow two WebRTC clients to find each other. 90// 91// The test also runs several other custom binaries - rgba_to_i420 converter and 92// frame_analyzer. Both tools can be found under third_party/webrtc/tools. The 93// test also runs a stand alone Python implementation of a WebSocket server 94// (pywebsocket) and a barcode_decoder script. 95class WebrtcVideoQualityBrowserTest : public WebRtcTestBase { 96 public: 97 WebrtcVideoQualityBrowserTest() 98 : pywebsocket_server_(0), 99 environment_(base::Environment::Create()) {} 100 101 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 102 PeerConnectionServerRunner::KillAllPeerConnectionServersOnCurrentSystem(); 103 } 104 105 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 106 // TODO(phoglund): check that user actually has the requisite devices and 107 // print a nice message if not; otherwise the test just times out which can 108 // be confusing. 109 // This test expects real device handling and requires a real webcam / audio 110 // device; it will not work with fake devices. 111 EXPECT_FALSE( 112 command_line->HasSwitch(switches::kUseFakeDeviceForMediaStream)) 113 << "You cannot run this test with fake devices."; 114 115 // The video playback will not work without a GPU, so force its use here. 116 command_line->AppendSwitch(switches::kUseGpuInTests); 117 } 118 119 bool HasAllRequiredResources() { 120 if (!base::PathExists(GetWorkingDir())) { 121 LOG(ERROR) << "Cannot find the working directory for the reference video " 122 "and the temporary files:" << GetWorkingDir().value(); 123 return false; 124 } 125 base::FilePath reference_file = 126 GetWorkingDir().Append(kReferenceYuvFileName); 127 if (!base::PathExists(reference_file)) { 128 LOG(ERROR) << "Cannot find the reference file to be used for video " 129 << "quality comparison: " << reference_file.value(); 130 return false; 131 } 132 return true; 133 } 134 135 bool StartPyWebSocketServer() { 136 base::FilePath path_pywebsocket_dir = 137 GetSourceDir().Append(FILE_PATH_LITERAL("third_party/pywebsocket/src")); 138 base::FilePath pywebsocket_server = path_pywebsocket_dir.Append( 139 FILE_PATH_LITERAL("mod_pywebsocket/standalone.py")); 140 base::FilePath path_to_data_handler = 141 GetSourceDir().Append(FILE_PATH_LITERAL("chrome/test/functional")); 142 143 if (!base::PathExists(pywebsocket_server)) { 144 LOG(ERROR) << "Missing pywebsocket server."; 145 return false; 146 } 147 if (!base::PathExists(path_to_data_handler)) { 148 LOG(ERROR) << "Missing data handler for pywebsocket server."; 149 return false; 150 } 151 152 AppendToPythonPath(path_pywebsocket_dir); 153 154 // Note: don't append switches to this command since it will mess up the 155 // -u in the python invocation! 156 CommandLine pywebsocket_command(CommandLine::NO_PROGRAM); 157 EXPECT_TRUE(GetPythonCommand(&pywebsocket_command)); 158 159 pywebsocket_command.AppendArgPath(pywebsocket_server); 160 pywebsocket_command.AppendArg("-p"); 161 pywebsocket_command.AppendArg(kPyWebSocketPortNumber); 162 pywebsocket_command.AppendArg("-d"); 163 pywebsocket_command.AppendArgPath(path_to_data_handler); 164 165 LOG(INFO) << "Running " << pywebsocket_command.GetCommandLineString(); 166 return base::LaunchProcess(pywebsocket_command, base::LaunchOptions(), 167 &pywebsocket_server_); 168 } 169 170 bool ShutdownPyWebSocketServer() { 171 return base::KillProcess(pywebsocket_server_, 0, false); 172 } 173 174 // Convenience method which executes the provided javascript in the context 175 // of the provided web contents and returns what it evaluated to. 176 std::string ExecuteJavascript(const std::string& javascript, 177 content::WebContents* tab_contents) { 178 std::string result; 179 EXPECT_TRUE(content::ExecuteScriptAndExtractString( 180 tab_contents, javascript, &result)); 181 return result; 182 } 183 184 // Ensures we didn't get any errors asynchronously (e.g. while no javascript 185 // call from this test was outstanding). 186 // TODO(phoglund): this becomes obsolete when we switch to communicating with 187 // the DOM message queue. 188 void AssertNoAsynchronousErrors(content::WebContents* tab_contents) { 189 EXPECT_EQ("ok-no-errors", 190 ExecuteJavascript("getAnyTestFailures()", tab_contents)); 191 } 192 193 // The peer connection server lets our two tabs find each other and talk to 194 // each other (e.g. it is the application-specific "signaling solution"). 195 void ConnectToPeerConnectionServer(const std::string peer_name, 196 content::WebContents* tab_contents) { 197 std::string javascript = base::StringPrintf( 198 "connect('http://localhost:8888', '%s');", peer_name.c_str()); 199 EXPECT_EQ("ok-connected", ExecuteJavascript(javascript, tab_contents)); 200 } 201 202 void EstablishCall(content::WebContents* from_tab, 203 content::WebContents* to_tab) { 204 EXPECT_EQ("ok-peerconnection-created", 205 ExecuteJavascript("preparePeerConnection()", from_tab)); 206 EXPECT_EQ("ok-added", ExecuteJavascript("addLocalStream()", from_tab)); 207 EXPECT_EQ("ok-negotiating", ExecuteJavascript("negotiateCall()", from_tab)); 208 209 // Ensure the call gets up on both sides. 210 EXPECT_TRUE(PollingWaitUntil( 211 "getPeerConnectionReadyState()", "active", from_tab)); 212 EXPECT_TRUE(PollingWaitUntil( 213 "getPeerConnectionReadyState()", "active", to_tab)); 214 } 215 216 void HangUp(content::WebContents* from_tab) { 217 EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab)); 218 } 219 220 void WaitUntilHangupVerified(content::WebContents* tab_contents) { 221 EXPECT_TRUE(PollingWaitUntil( 222 "getPeerConnectionReadyState()", "no-peer-connection", tab_contents)); 223 } 224 225 // Runs the RGBA to I420 converter on the video in |capture_video_filename|, 226 // which should contain frames of size |width| x |height|. 227 // 228 // The rgba_to_i420_converter is part of the webrtc_test_tools target which 229 // should be build prior to running this test. The resulting binary should 230 // live next to Chrome. 231 bool RunARGBtoI420Converter(int width, 232 int height, 233 const base::FilePath& captured_video_filename) { 234 base::FilePath path_to_converter = base::MakeAbsoluteFilePath( 235 GetBrowserDir().Append(kArgbToI420ConverterExecutable)); 236 237 if (!base::PathExists(path_to_converter)) { 238 LOG(ERROR) << "Missing ARGB->I420 converter: should be in " 239 << path_to_converter.value(); 240 return false; 241 } 242 243 CommandLine converter_command(path_to_converter); 244 converter_command.AppendSwitchPath("--frames_dir", GetWorkingDir()); 245 converter_command.AppendSwitchPath("--output_file", 246 captured_video_filename); 247 converter_command.AppendSwitchASCII("--width", 248 base::StringPrintf("%d", width)); 249 converter_command.AppendSwitchASCII("--height", 250 base::StringPrintf("%d", height)); 251 252 // We produce an output file that will later be used as an input to the 253 // barcode decoder and frame analyzer tools. 254 LOG(INFO) << "Running " << converter_command.GetCommandLineString(); 255 std::string result; 256 bool ok = base::GetAppOutput(converter_command, &result); 257 LOG(INFO) << "Output was:\n\n" << result; 258 return ok; 259 } 260 261 // Compares the |captured_video_filename| with the |reference_video_filename|. 262 // 263 // The barcode decoder decodes the captured video containing barcodes overlaid 264 // into every frame of the video (produced by rgba_to_i420_converter). It 265 // produces a set of PNG images and a |stats_file| that maps each captured 266 // frame to a frame in the reference video. The frames should be of size 267 // |width| x |height|. The output of compare_videos.py is returned. 268 bool CompareVideos(int width, 269 int height, 270 const base::FilePath& captured_video_filename, 271 const base::FilePath& reference_video_filename, 272 const base::FilePath& stats_file, 273 std::string* result) { 274 275 base::FilePath path_to_analyzer = base::MakeAbsoluteFilePath( 276 GetBrowserDir().Append(kFrameAnalyzerExecutable)); 277 base::FilePath path_to_compare_script = GetSourceDir().Append( 278 FILE_PATH_LITERAL("third_party/webrtc/tools/compare_videos.py")); 279 280 if (!base::PathExists(path_to_analyzer)) { 281 LOG(ERROR) << "Missing frame analyzer: should be in " 282 << path_to_analyzer.value(); 283 return false; 284 } 285 if (!base::PathExists(path_to_compare_script)) { 286 LOG(ERROR) << "Missing video compare script: should be in " 287 << path_to_compare_script.value(); 288 return false; 289 } 290 291 // Note: don't append switches to this command since it will mess up the 292 // -u in the python invocation! 293 CommandLine compare_command(CommandLine::NO_PROGRAM); 294 EXPECT_TRUE(GetPythonCommand(&compare_command)); 295 296 compare_command.AppendArgPath(path_to_compare_script); 297 compare_command.AppendArg("--ref_video"); 298 compare_command.AppendArgPath(reference_video_filename); 299 compare_command.AppendArg("--test_video"); 300 compare_command.AppendArgPath(captured_video_filename); 301 compare_command.AppendArg("--frame_analyzer"); 302 compare_command.AppendArgPath(path_to_analyzer); 303 compare_command.AppendArg("--yuv_frame_width"); 304 compare_command.AppendArg(base::StringPrintf("%d", width)); 305 compare_command.AppendArg("--yuv_frame_height"); 306 compare_command.AppendArg(base::StringPrintf("%d", height)); 307 compare_command.AppendArg("--stats_file"); 308 compare_command.AppendArgPath(stats_file); 309 310 LOG(INFO) << "Running " << compare_command.GetCommandLineString(); 311 bool ok = base::GetAppOutput(compare_command, result); 312 LOG(INFO) << "Output was:\n\n" << *result; 313 return ok; 314 } 315 316 // Processes the |frame_analyzer_output| for the different frame counts. 317 // 318 // The frame analyzer outputs additional information about the number of 319 // unique frames captured, The max number of repeated frames in a sequence and 320 // the max number of skipped frames. These values are then written to the Perf 321 // Graph. (Note: Some of the repeated or skipped frames will probably be due 322 // to the imperfection of JavaScript timers). 323 void PrintFramesCountPerfResults(std::string frame_analyzer_output) { 324 size_t unique_frames_pos = 325 frame_analyzer_output.rfind("Unique_frames_count"); 326 EXPECT_NE(unique_frames_pos, std::string::npos) 327 << "Missing Unique_frames_count in frame analyzer output:\n" 328 << frame_analyzer_output; 329 330 std::string unique_frame_counts = 331 frame_analyzer_output.substr(unique_frames_pos); 332 // TODO(phoglund): Fix ESTATS result to not have this silly newline. 333 std::replace( 334 unique_frame_counts.begin(), unique_frame_counts.end(), '\n', ' '); 335 336 std::vector<std::pair<std::string, std::string> > key_values; 337 base::SplitStringIntoKeyValuePairs( 338 unique_frame_counts, ':', ' ', &key_values); 339 std::vector<std::pair<std::string, std::string> >::const_iterator iter; 340 for (iter = key_values.begin(); iter != key_values.end(); ++iter) { 341 const std::pair<std::string, std::string>& key_value = *iter; 342 perf_test::PrintResult( 343 key_value.first, "", "VGA", key_value.second, "", false); 344 } 345 } 346 347 // Processes the |frame_analyzer_output| to extract the PSNR and SSIM values. 348 // 349 // The frame analyzer produces PSNR and SSIM results for every unique frame 350 // that has been captured. This method forms a list of all the psnr and ssim 351 // values and passes it to PrintResultList() for printing on the Perf Graph. 352 void PrintPsnrAndSsimPerfResults(std::string frame_analyzer_output) { 353 size_t stats_start = frame_analyzer_output.find("BSTATS"); 354 EXPECT_NE(stats_start, std::string::npos) 355 << "Missing BSTATS in frame analyzer output:\n" 356 << frame_analyzer_output; 357 size_t stats_end = frame_analyzer_output.find("ESTATS"); 358 EXPECT_NE(stats_end, std::string::npos) 359 << "Missing ESTATS in frame analyzer output:\n" 360 << frame_analyzer_output; 361 362 stats_start += std::string("BSTATS").size(); 363 std::string psnr_ssim_stats = 364 frame_analyzer_output.substr(stats_start, stats_end - stats_start); 365 366 // PSNR and SSIM values aren't really key-value pairs but it is convenient 367 // to parse them as such. 368 // TODO(phoglund): make the format more convenient so we need less 369 // processing here. 370 std::vector<std::pair<std::string, std::string> > psnr_ssim_entries; 371 base::SplitStringIntoKeyValuePairs( 372 psnr_ssim_stats, ' ', ';', &psnr_ssim_entries); 373 374 std::string psnr_value_list; 375 std::string ssim_value_list; 376 std::vector<std::pair<std::string, std::string> >::const_iterator iter; 377 for (iter = psnr_ssim_entries.begin(); iter != psnr_ssim_entries.end(); 378 ++iter) { 379 const std::pair<std::string, std::string>& psnr_and_ssim = *iter; 380 psnr_value_list.append(psnr_and_ssim.first).append(","); 381 ssim_value_list.append(psnr_and_ssim.second).append(","); 382 } 383 384 // Nuke last comma. 385 ASSERT_GT(psnr_value_list.size(), 0u) << "Received no valid PSNR values."; 386 ASSERT_GT(ssim_value_list.size(), 0u) << "Received no valid SSIM values."; 387 psnr_value_list.erase(psnr_value_list.size() - 1); 388 ssim_value_list.erase(ssim_value_list.size() - 1); 389 390 perf_test::PrintResultList("PSNR", "", "VGA", psnr_value_list, "dB", false); 391 perf_test::PrintResultList("SSIM", "", "VGA", ssim_value_list, "", false); 392 } 393 394 base::FilePath GetWorkingDir() { 395 std::string home_dir; 396 environment_->GetVar(kHomeEnvName, &home_dir); 397 base::FilePath::StringType native_home_dir(home_dir.begin(), 398 home_dir.end()); 399 return base::FilePath(native_home_dir).Append(kWorkingDirName); 400 } 401 402 PeerConnectionServerRunner peerconnection_server_; 403 404 private: 405 base::FilePath GetSourceDir() { 406 base::FilePath source_dir; 407 PathService::Get(base::DIR_SOURCE_ROOT, &source_dir); 408 return source_dir; 409 } 410 411 base::FilePath GetBrowserDir() { 412 base::FilePath browser_dir; 413 EXPECT_TRUE(PathService::Get(base::DIR_MODULE, &browser_dir)); 414 return browser_dir; 415 } 416 417 base::ProcessHandle pywebsocket_server_; 418 scoped_ptr<base::Environment> environment_; 419}; 420 421IN_PROC_BROWSER_TEST_F(WebrtcVideoQualityBrowserTest, 422 MANUAL_TestVGAVideoQuality) { 423 ASSERT_TRUE(HasAllRequiredResources()); 424 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 425 ASSERT_TRUE(StartPyWebSocketServer()); 426 ASSERT_TRUE(peerconnection_server_.Start()); 427 428 ui_test_utils::NavigateToURL( 429 browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage)); 430 content::WebContents* left_tab = 431 browser()->tab_strip_model()->GetActiveWebContents(); 432 GetUserMediaAndAccept(left_tab); 433 434 chrome::AddBlankTabAt(browser(), -1, true); 435 content::WebContents* right_tab = 436 browser()->tab_strip_model()->GetActiveWebContents(); 437 ui_test_utils::NavigateToURL( 438 browser(), embedded_test_server()->GetURL(kCapturingWebrtcHtmlPage)); 439 GetUserMediaAndAccept(right_tab); 440 441 ConnectToPeerConnectionServer("peer 1", left_tab); 442 ConnectToPeerConnectionServer("peer 2", right_tab); 443 444 EstablishCall(left_tab, right_tab); 445 446 AssertNoAsynchronousErrors(left_tab); 447 AssertNoAsynchronousErrors(right_tab); 448 449 // Poll slower here to avoid flooding the log with messages: capturing and 450 // sending frames take quite a bit of time. 451 int polling_interval_msec = 1000; 452 453 EXPECT_TRUE(PollingWaitUntil( 454 "doneFrameCapturing()", "done-capturing", right_tab, 455 polling_interval_msec)); 456 457 HangUp(left_tab); 458 WaitUntilHangupVerified(left_tab); 459 WaitUntilHangupVerified(right_tab); 460 461 AssertNoAsynchronousErrors(left_tab); 462 AssertNoAsynchronousErrors(right_tab); 463 464 EXPECT_TRUE(PollingWaitUntil( 465 "haveMoreFramesToSend()", "no-more-frames", right_tab, 466 polling_interval_msec)); 467 468 RunARGBtoI420Converter( 469 kVgaWidth, kVgaHeight, GetWorkingDir().Append(kCapturedYuvFileName)); 470 std::string output; 471 ASSERT_TRUE( 472 CompareVideos(kVgaWidth, 473 kVgaHeight, 474 GetWorkingDir().Append(kCapturedYuvFileName), 475 GetWorkingDir().Append(kReferenceYuvFileName), 476 GetWorkingDir().Append(kStatsFileName), 477 &output)); 478 479 PrintFramesCountPerfResults(output); 480 PrintPsnrAndSsimPerfResults(output); 481 482 ASSERT_TRUE(peerconnection_server_.Stop()); 483 ASSERT_TRUE(ShutdownPyWebSocketServer()); 484} 485