1 # Copyright (c) 2010 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 5 import glob, logging, os, re, stat 6 from autotest_lib.client.bin import test, utils 7 from autotest_lib.client.common_lib import error 8 from autotest_lib.client.cros.graphics import graphics_utils 9 10 11 class camera_V4L2(test.test): 12 version = 1 13 preserve_srcdir = True 14 v4l2_major_dev_num = 81 15 v4l2_minor_dev_num_min = 0 16 v4l2_minor_dev_num_max = 64 17 18 19 def setup(self): 20 # TODO(jiesun): make binary here when cross compile issue is resolved. 21 os.chdir(self.srcdir) 22 utils.make('clean') 23 utils.make() 24 25 26 def run_once(self, run_unit_tests=True, run_capture_tests=True, 27 run_default_capture_test=False, time=0, 28 assert_mandatory_controls=False): 29 30 self.assert_mandatory_controls = assert_mandatory_controls 31 self.find_video_capture_devices() 32 time = time / len(self.v4l2_devices) 33 34 for device in self.v4l2_devices: 35 if run_unit_tests: 36 self.run_v4l2_unittests(device) 37 if run_capture_tests: 38 self.run_v4l2_capture_tests(device) 39 if run_default_capture_test: 40 self.run_v4l2_default_capture_test(device, time) 41 42 43 def is_v4l2_capture_device(self, device): 44 executable = os.path.join(self.bindir, "media_v4l2_is_capture_device") 45 cmd = "%s %s" % (executable, device) 46 logging.info("Running %s" % cmd) 47 return (utils.system(cmd, ignore_status=True) == 0) 48 49 50 def find_video_capture_devices(self): 51 self.v4l2_devices = [] 52 for device in glob.glob("/dev/video*"): 53 statinfo = os.stat(device) 54 if (stat.S_ISCHR(statinfo.st_mode) and 55 os.major(statinfo.st_rdev) == self.v4l2_major_dev_num and 56 os.minor(statinfo.st_rdev) >= self.v4l2_minor_dev_num_min and 57 os.minor(statinfo.st_rdev) < self.v4l2_minor_dev_num_max and 58 self.is_v4l2_capture_device(device)): 59 self.v4l2_devices.append(device) 60 logging.info("Detected devices: %s\n" % self.v4l2_devices) 61 if not self.v4l2_devices: 62 raise error.TestFail("No V4L2 devices found!") 63 64 65 def unittest_passed(self, testname, stdout): 66 return re.search(r"OK \] V4L2DeviceTest\." + testname, stdout); 67 68 69 def run_v4l2_unittests(self, device): 70 self.executable = os.path.join(self.bindir, "media_v4l2_unittest") 71 cmd = "%s --device=%s" % (self.executable, device) 72 logging.info("Running %s" % cmd) 73 stdout = utils.system_output(cmd, retain_output=True) 74 75 # Check the result of unittests. 76 # We had exercise all the optional ioctls in unittest which maybe 77 # optional by V4L2 Specification. Therefore we need to check those 78 # tests that we thought are mandatory. 79 # 1. Multiple open should be supported for panel application. 80 if not self.unittest_passed("MultipleOpen", stdout): 81 raise error.TestError(device + " does not support multiple open!") 82 83 # 2. Need to make sure this is really support or just driver error. 84 if not self.unittest_passed("MultipleInit", stdout): 85 raise error.TestError(device + " does support multiple init!") 86 87 # 3. EnumInput and EnumStandard is optional. 88 89 # 4. EnumControl is mandatory. 90 if not self.unittest_passed("EnumControl", stdout): 91 raise error.TestError(device + " does support enum controls!") 92 pattern = re.compile(r"Control (\w+) is enabled\((\d+)-(\d+):(\d+)\)") 93 control_info = pattern.findall(stdout) 94 self.supported_controls = [ x[0] for x in control_info ] 95 logging.info("Supported Controls: %s\n" % self.supported_controls) 96 97 # TODO(jiesun): what is required? 98 mandatory_controls = [ 99 "Brightness", 100 "Contrast", 101 "Saturation", 102 "Hue", 103 "Gamma"] 104 for control in mandatory_controls: 105 if self.assert_mandatory_controls and \ 106 control not in self.supported_controls: 107 raise error.TestError(device + " does not support " + control) 108 109 # 5. SetControl is mandatory. 110 if not self.unittest_passed("SetControl", stdout): 111 raise error.TestError(device + " does not support set controls!") 112 113 # 6. 7. Set/GetCrop are both optional. 114 115 # 8. ProbeCaps is mandatory. 116 if not self.unittest_passed("ProbeCaps", stdout): 117 raise error.TestError(device + " does not support probe caps!") 118 119 if not re.search(r"support video capture interface.>>>", stdout): 120 raise error.TestFail(device + " does not support video capture!") 121 122 pattern = r"support streaming i/o interface.>>>" 123 self.support_streaming = True if re.search(pattern, stdout) else False 124 125 pattern = r"support streaming read/write interface.>>>" 126 self.support_readwrite = True if re.search(pattern, stdout) else False 127 128 # Currently I assume streaming (mmap) is mandatroy. 129 if not self.support_streaming: 130 raise error.TestFail(device + " does not support streaming!") 131 132 # 9. EnumFormats is always mandatory. 133 if not self.unittest_passed("EnumFormats", stdout): 134 raise error.TestError(device + " does not support enum formats!") 135 136 pattern = re.compile(r"supported format #\d+: .* \((....)\)") 137 format_info = pattern.findall(stdout) 138 # Remove duplicated pixel formats from list. 139 self.supported_formats = list(set(format_info)) 140 logging.info("Supported pixel format: %s\n", self.supported_formats) 141 142 # 10. Get/SetParam for framerate is optional. 143 # 11. EnumFrameSize is optional on some kernel/v4l2 version. 144 145 146 def run_v4l2_capture_test(self, fail_okay, options): 147 executable = os.path.join(self.bindir, "media_v4l2_test") 148 try: 149 cmd = "%s %s" % (executable, " ".join(options)) 150 cmd = graphics_utils.xcommand(cmd) 151 logging.info("Running %s" % cmd) 152 stdout = utils.system_output(cmd, retain_output=True) 153 except: 154 if fail_okay: 155 stdout = "" 156 return (False, stdout) 157 else: 158 raise 159 else: 160 return (True, stdout) 161 162 163 def run_v4l2_default_capture_test(self, device, time): 164 options = ["--device=%s" % device ] 165 if time: 166 options.append("--time=%d" % time) 167 okay, stdout = self.run_v4l2_capture_test(False, options) 168 169 170 def run_v4l2_capture_tests(self, device): 171 default_options = ["--device=%s" % device ] 172 173 # If the device claims to support read/write i/o. 174 if self.support_readwrite: 175 option = default_options + ["--read"] 176 okay, stdout = self.run_v4l2_capture_test(False, option) 177 178 # If the device claims to support stream i/o. 179 # This could mean either mmap stream i/o or user pointer stream i/o. 180 if self.support_streaming: 181 option = default_options + ["--mmap"] 182 mmap_okay, stdout = self.run_v4l2_capture_test(True, option) 183 184 option = default_options + ["--userp"] 185 userp_okay, stdout = self.run_v4l2_capture_test(True, option) 186 187 if not userp_okay and not mmap_okay: 188 raise error.TestFail("Stream i/o failed!") 189 190 191 # TODO(jiesun): test with different mandatory resultions that 192 # the capture device must support without scaling by ourselves. 193 required_resolutions = [ 194 (320, 240, 30), # QVGA 195 (640, 480, 30)] # VGA 196 for (width, height, minfps) in required_resolutions: 197 # Note use default mmap i/o here. 198 option = default_options[:] 199 # Note use first supported pixel format. 200 option.append("--pixel-format=%s" % self.supported_formats[0]) 201 option.append("--width=%s" % width) 202 option.append("--height=%s" % height) 203 okay, stdout = self.run_v4l2_capture_test(False, option) 204 # Check if the actual format is desired. 205 pattern = (r"actual format for capture (\d+)x(\d+)" 206 r" (....) picture at (\d+) fps") 207 match = re.search(pattern, stdout) 208 if (not match or 209 int(match.group(1)) != width or 210 int(match.group(2)) != height or 211 match.group(3) != self.supported_formats[0] or 212 int(match.group(4)) < minfps): 213 raise error.TestError("capture test failed") 214 215 okay, stdout = self.run_v4l2_capture_test(False, option) 216