Greatly simplify webrtc peerconnection test.
I don't see any reason to exercise 720p if it is available,
since video_WebRtcCamera is already doing that. I'm able to
greatly simplify the test (which will be nice if I add h264
support later).
Also fixes broken error handling and removes unnecessary
resolution management (video tags take the appropriate size
automatically, gUM selects a suitable constraint automatically
among the available ones, etc).
Also moves resolution check code to camera test, where it
belongs.
BUG=chromium:625241,chromium:628630
TEST=locally on link laptop
Change-Id: I14ba78fcb137ee688be4e82ac7ca1ceedb1634cc
Reviewed-on: https://chromium-review.googlesource.com/411842
Commit-Ready: Patrik Höglund <phoglund@chromium.org>
Commit-Ready: Rohit Makasana <rohitbm@chromium.org>
Tested-by: Rohit Makasana <rohitbm@chromium.org>
Reviewed-by: Rohit Makasana <rohitbm@chromium.org>
diff --git a/client/site_tests/video_WebRtcCamera/getusermedia.html b/client/site_tests/video_WebRtcCamera/getusermedia.html
index c23e53c..bd64ebf 100644
--- a/client/site_tests/video_WebRtcCamera/getusermedia.html
+++ b/client/site_tests/video_WebRtcCamera/getusermedia.html
@@ -2,7 +2,7 @@
<html>
<head><title>Loopback test</title></head>
<body>
- <video id="localVideo" width="1280" height="720" autoplay muted></video>
+ <video id="localVideo" autoplay muted></video>
<script src="ssim.js"></script>
<script src="blackframe.js"></script>
<script>
@@ -39,6 +39,12 @@
return results;
}
+function resolutionMatchesIndependentOfRotation(aWidth, aHeight,
+ bWidth, bHeight) {
+ return (aWidth === bWidth && aHeight === bHeight) ||
+ (aWidth === bHeight && aHeight === bWidth);
+}
+
function saveResult(resolution, verdict) {
results[resolution] = verdict;
}
@@ -65,7 +71,9 @@
"video": {
"mandatory" : {
"maxWidth": this.resolution[0].toString(),
- "maxHeight": this.resolution[1].toString()
+ "maxHeight": this.resolution[1].toString(),
+ "minWidth": this.resolution[0].toString(),
+ "minHeight": this.resolution[1].toString()
},
}
};
@@ -94,6 +102,14 @@
},
startCheckingVideoFrames: function() {
+ if (!resolutionMatchesIndependentOfRotation(this.localVideo.videoWidth,
+ this.localVideo.videoHeight, this.resolution[0], this.resolution[1])) {
+ this.results.cameraErrors.push('resolution', 'Got ' +
+ this.localVideo.videoWidth + 'x' + this.localVideo.videoHeight +
+ ', expected ' + this.resolution[0] + 'x' + this.resolution[1] +
+ ' or rotated version thereof');
+ }
+
this.videoFrameChecker = setInterval(this.checkVideoFrame.bind(this), 20);
},
diff --git a/client/site_tests/video_WebRtcPeerConnectionWithCamera/control b/client/site_tests/video_WebRtcPeerConnectionWithCamera/control
index f24d3ec..264e4b2 100644
--- a/client/site_tests/video_WebRtcPeerConnectionWithCamera/control
+++ b/client/site_tests/video_WebRtcPeerConnectionWithCamera/control
@@ -4,8 +4,8 @@
AUTHOR = "Chrome OS Project, chromeos-video@google.com"
NAME = "video_WebRtcPeerConnectionWithCamera"
-PURPOSE = "Check WebRTC local peer connection can be established"
-CRITERIA = "This test will fail if WebRTC peer connection not established"
+PURPOSE = "Ensure WebRTC call gets up and produces healthy video"
+CRITERIA = "Fails if the call doesn't even get up."
ATTRIBUTES = "suite:av_webcam"
TIME = "MEDIUM"
TEST_CATEGORY = "Performance"
@@ -18,11 +18,16 @@
}
DOC = """
-This test starts a local WebRTC call with two peer connections and ensures
-we get the right width and height for the video stream. This is done by
-checking both the video tag's attributes and peer connection stats
-(using getStats). Unless there's a direct failure, stats for the call are
-reported to the performance dashboard.
+This test starts a loopback WebRTC call with two peer connections
+and ensures it successfully establishes the call (otherwise the test
+will simply fail). If successful, it looks at the video frames coming
+out on the receiving side of the call and looks for freezes and black
+frames. If this test shows black frames and video_WebRtcCamera does not,
+it could mean video isn't encoded/decoded right on this device but that
+the camera works. Finally, input and output FPS are logged.
+
+Black frames/freezes/fps are reported to the perf dashboard and
+must be monitored there.
"""
job.run_test("video_WebRtcPeerConnectionWithCamera")
diff --git a/client/site_tests/video_WebRtcPeerConnectionWithCamera/loopback.html b/client/site_tests/video_WebRtcPeerConnectionWithCamera/loopback.html
index 5ab5826..2c09bca 100644
--- a/client/site_tests/video_WebRtcPeerConnectionWithCamera/loopback.html
+++ b/client/site_tests/video_WebRtcPeerConnectionWithCamera/loopback.html
@@ -2,19 +2,18 @@
<html>
<head><title>Loopback test</title></head>
<body>
- <video id="localVideo" width="1280" height="720" autoplay muted></video>
- <video id="remoteVideo" width="1280" height="720" autoplay muted></video>
+ <video id="localVideo" autoplay muted></video>
+ <video id="remoteVideo" autoplay muted></video>
<script src="ssim.js"></script>
<script src="blackframe.js"></script>
<script>
-
var results = {};
-var testProgress = 0;
+var testStatus = 'running';
// Starts the test.
-function testCamera(resolution) {
- var test = new CameraTest(resolution);
+function testWebRtcLoopbackCall() {
+ var test = new WebRtcLoopbackCallTest();
test.run();
}
@@ -27,6 +26,10 @@
results = stats;
}
+function getStatus() {
+ return testStatus;
+}
+
// Calculates averages of array values.
function average(array) {
var count = array.length;
@@ -38,11 +41,10 @@
}
// Actual test object.
-function CameraTest(resolutionArray) {
- this.resolution = resolutionArray;
+function WebRtcLoopbackCallTest() {
this.localStream = null;
this.remoteStream = null;
- this.results = {cameraType: '', cameraErrors: [], peerConnectionStats: [],
+ this.results = {cameraType: '', peerConnectionStats: [],
frameStats: {numBlackFrames: 0, numFrozenFrames:0, numFrames: 0}};
this.inFps = [];
@@ -54,27 +56,10 @@
this.remoteVideo = document.getElementById("remoteVideo");
this.localVideo = document.getElementById("localVideo");
- this.localVideo.width = this.resolution[0].toString();
- this.localVideo.height = this.resolution[1].toString();
- this.remoteVideo.width = this.resolution[0].toString();
- this.remoteVideo.height = this.resolution[1].toString();
}
-function resolutionMatchesIndependentOfRotation(aWidth, aHeight,
- bWidth, bHeight) {
- return (aWidth === bWidth && aHeight === bHeight) ||
- (aWidth === bHeight && aHeight === bWidth);
-}
-
-CameraTest.prototype = {
+WebRtcLoopbackCallTest.prototype = {
collectAndAnalyzeStats: function() {
- if (!resolutionMatchesIndependentOfRotation(this.localVideo.width,
- this.localVideo.height, this.resolution[0], this.resolution[1])) {
- this.reportError('resolution', 'Got resolution ' + this.resolution[0] +
- + 'x' + this.resolution[1] + ', expected resolution' +
- this.localVideo.width + 'x' + this.localVideo.height +
- ' or rotated version thereof');
- }
this.gatherStats(this.localPeerConnection, 100, 20000,
this.reportTestDone.bind(this));
},
@@ -96,39 +81,24 @@
run: function() {
this.setup();
- this.triggerGetUserMedia(this.resolution);
+ this.triggerGetUserMedia();
},
- triggerGetUserMedia: function(resolution) {
- var constraints = {
- audio: false,
- video: {
- mandatory: {
- minWidth: resolution[0],
- minHeight: resolution[1],
- maxWidth: resolution[0],
- maxHeight: resolution[1]
- }
- }
- };
+ triggerGetUserMedia: function() {
+ var constraints = {audio: false, video: true};
try {
- this.doGetUserMedia(constraints, this.gotLocalStream.bind(this),
+ navigator.getUserMedia = navigator.getUserMedia ||
+ navigator.webkitGetUserMedia;
+ navigator.getUserMedia(constraints, this.gotLocalStream.bind(this),
this.onGetUserMediaError.bind(this));
} catch (exception) {
- console.log('Unexpected exception: ', exception);
- this.reportError('gUM', 'doGetUserMedia failed: ' + exception);
+ this.reportError('getUserMedia exception: ' + exception.toString());
}
},
- reportError: function(errorType, message) {
- this.results.cameraErrors.push([errorType, message]);
- console.log(message);
- },
-
- doGetUserMedia: function(constraints, onSuccess, onFail) {
- navigator.getUserMedia = navigator.getUserMedia ||
- navigator.webkitGetUserMedia;
- navigator.getUserMedia(constraints, onSuccess, onFail);
+ reportError: function(message) {
+ console.error(message);
+ testStatus = message;
},
gotLocalStream: function(stream) {
@@ -152,8 +122,8 @@
this.results.cameraType = stream.getVideoTracks()[0].label;
},
- onGetUserMediaError: function(stream) {
- this.reportError('gUM', 'gUM call failed');
+ onGetUserMediaError: function(error) {
+ this.reportError('getUserMedia failed: ' + error.toString());
},
gatherStats: function(peerConnection, interval, durationMs, callback) {
@@ -202,7 +172,7 @@
setResults(this.results);
- testProgress = 1;
+ testStatus = 'ok-done';
},
processStats: function() {
@@ -280,9 +250,11 @@
},
}
+
window.onerror = function (message, filename, lineno, colno, error) {
console.log("Something went wrong, here is the stack trace --> %s",
- error.stack);
+ error.stack);
+ testStatus = 'exception-in-test-page: ' + error.stack
};
</script>
</body>
diff --git a/client/site_tests/video_WebRtcPeerConnectionWithCamera/video_WebRtcPeerConnectionWithCamera.py b/client/site_tests/video_WebRtcPeerConnectionWithCamera/video_WebRtcPeerConnectionWithCamera.py
index 0cf68d6..def72517 100644
--- a/client/site_tests/video_WebRtcPeerConnectionWithCamera/video_WebRtcPeerConnectionWithCamera.py
+++ b/client/site_tests/video_WebRtcPeerConnectionWithCamera/video_WebRtcPeerConnectionWithCamera.py
@@ -13,18 +13,12 @@
EXTRA_BROWSER_ARGS = ['--use-fake-ui-for-media-stream']
-# Statistics from the loopback.html page.
-TEST_PROGRESS = 'testProgress'
-
# Polling timeout.
TIMEOUT = 90
-RES_720P = [1280, 720]
-RES_VGA = [640, 480]
-
class video_WebRtcPeerConnectionWithCamera(test.test):
- """Local Peer connection test with webcam at 720p."""
+ """Tests a full WebRTC call with a real webcam."""
version = 1
def start_loopback(self, cr):
@@ -38,110 +32,48 @@
self.tab.Navigate(cr.browser.platform.http_server.UrlOf(
os.path.join(self.bindir, 'loopback.html')))
self.tab.WaitForDocumentReadyStateToBeComplete()
- self.tab.EvaluateJavaScript("testCamera(%s)" %
- self.chosen_resolution)
+ self.tab.EvaluateJavaScript("testWebRtcLoopbackCall()")
+ def wait_test_completed(self, timeout_secs):
+ """Waits until the test is done.
- def webcam_supports_720p(self):
- """Checks if 720p capture supported.
+ @param timeout_secs Max time to wait in seconds.
- @returns: True if 720p supported, false if VGA is supported.
- @raises: TestError if neither 720p nor VGA are supported.
- """
- cmd = 'lsusb -v'
- # Get usb devices and make output a string with no newline marker.
- usb_devices = utils.system_output(cmd, ignore_status=True).splitlines()
- usb_devices = ''.join(usb_devices)
-
- # Check if 720p resolution supported.
- if re.search(r'\s+wWidth\s+1280\s+wHeight\s+720', usb_devices):
- return True
- # The device should support at least VGA.
- # Otherwise the cam must be broken.
- if re.search(r'\s+wWidth\s+640\s+wHeight\s+480', usb_devices):
- return False
- # This should not happen.
- raise error.TestFail(
- 'Could not find any cameras reporting '
- 'either VGA or 720p in lsusb output: %s' % usb_devices)
-
-
- def is_test_completed(self):
- """Checks if WebRTC peerconnection test is done.
-
- @returns True if test complete, False otherwise.
-
+ @raises TestError on timeout, or javascript eval fails.
"""
def test_done():
- """Check the testProgress variable in HTML page."""
+ status = self.tab.EvaluateJavaScript('getStatus()')
+ logging.debug(status);
+ return status != 'running'
- # Wait for test completion on web page.
- test_progress = self.tab.EvaluateJavaScript(TEST_PROGRESS)
- return test_progress == 1
-
- try:
- utils.poll_for_condition(
- test_done, timeout=TIMEOUT,
- exception=error.TestError('Cannot find testProgress value.'),
- sleep_interval=1)
- except error.TestError:
- partial_results = self.tab.EvaluateJavaScript('getResults()')
- logging.info('Here are the partial results so far: %s',
- partial_results)
- return False
- else:
- return True
-
+ utils.poll_for_condition(
+ test_done, timeout=timeout_secs, sleep_interval=1,
+ desc='loopback.html reports itself as finished')
def run_once(self):
"""Runs the video_WebRtcPeerConnectionWithCamera test."""
- # Check webcamera resolution capabilities.
- # Some laptops have low resolution capture.
- if self.webcam_supports_720p():
- self.chosen_resolution = RES_720P
- else:
- self.chosen_resolution = RES_VGA
with chrome.Chrome(extra_browser_args=EXTRA_BROWSER_ARGS) as cr:
- # Open WebRTC loopback page and start the loopback.
self.start_loopback(cr)
- ok, message = self.print_loopback_result()
- if not ok:
- logging.error(message)
- raise error.TestFail(
- 'Failed at resolution %s because %s' %
- (self.chosen_resolution, message)
- )
-
+ self.wait_test_completed(TIMEOUT)
+ self.print_loopback_result()
def print_loopback_result(self):
- """Prints loopback results (unless we failed to retreieve them).
+ """Prints loopback results (unless we failed to retrieve them).
- This method prints the same perf descriptions regardless of which
- resolution the test was running in (which depends on device
- capabilities).
-
- Returns: a tuple (ok, message) where ok is false if we failed to
- retrieve any of the stats.
+ @raises TestError if the test failed outright.
"""
- if not self.is_test_completed():
- return False, 'loopback.html did not complete'
+ status = self.tab.EvaluateJavaScript('getStatus()')
+ if status != 'ok-done':
+ raise error.TestFail('Failed: %s' % status)
- try:
- results = self.tab.EvaluateJavaScript('getResults()')
- except:
- return False, 'Cannot retrieve results from loopback.html page'
-
+ results = self.tab.EvaluateJavaScript('getResults()')
logging.info('Camera Type: %s', results['cameraType'])
- logging.info('Camera Errors: %s', results['cameraErrors'])
logging.info('PeerConnectionstats: %s', results['peerConnectionStats'])
logging.info('FrameStats: %s', results['frameStats'])
- if results['cameraErrors']:
- return False, 'of camera error: %s' % results['cameraErrors']
-
pc_stats = results.get('peerConnectionStats')
if not pc_stats:
- return False, 'Peer Connection Stats is empty'
+ raise error.TestFail('Peer Connection Stats is empty')
self.output_perf_value(
description='max_input_fps', value=pc_stats[1], units='fps',
higher_is_better=True)
@@ -151,7 +83,7 @@
frame_stats = results.get('frameStats')
if not frame_stats:
- return False, 'Frame Stats is empty'
+ raise error.TestFail('Frame Stats is empty')
self.output_perf_value(
description='black_frames',
value=frame_stats['numBlackFrames'],
@@ -164,5 +96,3 @@
description='total_num_frames',
value=frame_stats['numFrames'],
units='frames', higher_is_better=True)
-
- return True, "All good"