1# Copyright 2015 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import re 8import time 9 10from autotest_lib.client.bin import test 11from autotest_lib.client.bin import utils 12from autotest_lib.client.common_lib import error 13from autotest_lib.client.common_lib.cros import chrome 14from autotest_lib.client.cros.video import device_capability 15from autotest_lib.client.cros.video import helper_logger 16 17EXTRA_BROWSER_ARGS = ['--use-fake-ui-for-media-stream'] 18 19# Variables from the getusermedia.html page. 20IS_TEST_DONE = 'isTestDone' 21IS_VIDEO_INPUT_FOUND = 'isVideoInputFound' 22 23# Polling timeout. 24SEVERAL_MINUTES_IN_SECS = 240 25 26 27class video_WebRtcCamera(test.test): 28 """Local getUserMedia test with webcam at VGA (and 720p, if supported).""" 29 version = 1 30 31 def cleanup(self): 32 """Autotest cleanup function 33 34 It is run by common_lib/test.py. 35 """ 36 if utils.is_virtual_machine(): 37 try: 38 utils.run('sudo modprobe -r vivid') 39 except Exception as e: 40 raise error.TestFail('Failed to unload vivid', e) 41 42 def start_getusermedia(self, cr): 43 """Opens the test page. 44 45 @param cr: Autotest Chrome instance. 46 """ 47 cr.browser.platform.SetHTTPServerDirectories(self.bindir) 48 49 self.tab = cr.browser.tabs[0] 50 self.tab.Navigate(cr.browser.platform.http_server.UrlOf( 51 os.path.join(self.bindir, 'getusermedia.html'))) 52 self.tab.WaitForDocumentReadyStateToBeComplete() 53 54 if utils.is_virtual_machine(): 55 # Before calling 'getUserMedia()' again, make sure if Chrome has 56 # already recognized vivid as an external camera. 57 self.wait_camera_detected() 58 59 # Reload the page to run 'getUserMedia()' again 60 self.tab.EvaluateJavaScript('location.reload()') 61 self.tab.WaitForDocumentReadyStateToBeComplete() 62 63 def wait_camera_detected(self): 64 """Waits until a camera is detected. 65 """ 66 for _ in range(10): 67 self.tab.EvaluateJavaScript('checkVideoInput()') 68 if self.tab.EvaluateJavaScript(IS_VIDEO_INPUT_FOUND): 69 return 70 time.sleep(1) 71 72 raise error.TestFail('Can not find video input device') 73 74 def webcam_supports_720p(self): 75 """Checks if 720p capture supported. 76 77 @returns: True if 720p supported, false if VGA is supported. 78 @raises: TestError if neither 720p nor VGA are supported. 79 """ 80 cmd = 'lsusb -v' 81 # Get usb devices and make output a string with no newline marker. 82 usb_devices = utils.system_output(cmd, ignore_status=True).splitlines() 83 usb_devices = ''.join(usb_devices) 84 85 # Check if 720p resolution supported. 86 if re.search(r'\s+wWidth\s+1280\s+wHeight\s+720', usb_devices): 87 return True 88 # The device should support at least VGA. 89 # Otherwise the cam must be broken. 90 if re.search(r'\s+wWidth\s+640\s+wHeight\s+480', usb_devices): 91 return False 92 # This should not happen. 93 raise error.TestFail( 94 'Could not find any cameras reporting ' 95 'either VGA or 720p in lsusb output: %s' % usb_devices) 96 97 98 def wait_test_completed(self, timeout_secs): 99 """Waits until the test is done. 100 101 @param timeout_secs Max time to wait in seconds. 102 103 @raises TestError on timeout, or javascript eval fails. 104 """ 105 def _test_done(): 106 return self.tab.EvaluateJavaScript(IS_TEST_DONE) 107 108 utils.poll_for_condition( 109 _test_done, timeout=timeout_secs, sleep_interval=1, 110 desc=('getusermedia.html:reportTestDone did not run. Test did not ' 111 'complete successfully.')) 112 113 @helper_logger.video_log_wrapper 114 def run_once(self, capability): 115 """Runs the test. 116 117 @param capability: Capability required for executing this test. 118 """ 119 device_capability.DeviceCapability().ensure_capability(capability) 120 121 self.board = utils.get_current_board() 122 with chrome.Chrome(extra_browser_args=EXTRA_BROWSER_ARGS +\ 123 [helper_logger.chrome_vmodule_flag()], 124 init_network_controller=True) as cr: 125 126 # TODO(keiichiw): vivid should be loaded in self.setup() after 127 # crbug.com/871185 is fixed 128 if utils.is_virtual_machine(): 129 try: 130 utils.run('sudo modprobe vivid n_devs=1 node_types=0x1') 131 except Exception as e: 132 raise error.TestFail('Failed to load vivid', e) 133 134 self.start_getusermedia(cr) 135 self.print_perf_results() 136 137 138 def print_perf_results(self): 139 """Prints the perf results unless there was an error. 140 141 @returns the empty string if perf results were printed, otherwise 142 a description of the error that occured. 143 """ 144 self.wait_test_completed(SEVERAL_MINUTES_IN_SECS) 145 try: 146 results = self.tab.EvaluateJavaScript('getResults()') 147 except Exception as e: 148 raise error.TestFail('Failed to get getusermedia.html results', e) 149 logging.info('Results: %s', results) 150 151 errors = [] 152 for width_height, data in results.iteritems(): 153 resolution = re.sub(',', 'x', width_height) 154 if data['cameraErrors']: 155 if (resolution == '1280x720' and 156 not self.webcam_supports_720p()): 157 logging.info('Accepting 720p failure since device webcam ' 158 'does not support 720p') 159 continue 160 161 # Else we had a VGA failure or a legit 720p failure. 162 errors.append('Camera error: %s for resolution ' 163 '%s.' % (data['cameraErrors'], resolution)) 164 continue 165 if not data['frameStats']: 166 errors.append('Frame Stats is empty ' 167 'for resolution: %s' % resolution) 168 continue 169 170 total_num_frames = data['frameStats']['numFrames'] 171 num_black_frames = data['frameStats']['numBlackFrames'] 172 num_frozen_frames = data['frameStats']['numFrozenFrames'] 173 174 def _percent(num, total): 175 if total == 0: 176 return 1.0 177 return float(num) / float(total) 178 179 self.output_perf_value( 180 description='black_frames_percentage_%s' % resolution, 181 value=_percent(num_black_frames, total_num_frames), 182 units='percent', higher_is_better=False) 183 self.output_perf_value( 184 description='frozen_frames_percentage_%s' % resolution, 185 value=_percent(num_frozen_frames, total_num_frames), 186 units='percent', higher_is_better=False) 187 188 if errors: 189 raise error.TestFail('Found errors: %s' % ', '.join(errors)) 190