# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import glob, logging, os, re, stat from autotest_lib.client.bin import test, utils from autotest_lib.client.common_lib import error from autotest_lib.client.cros.graphics import graphics_utils class camera_V4L2(test.test): version = 1 preserve_srcdir = True v4l2_major_dev_num = 81 v4l2_minor_dev_num_min = 0 v4l2_minor_dev_num_max = 64 def setup(self): # TODO(jiesun): make binary here when cross compile issue is resolved. os.chdir(self.srcdir) utils.make('clean') utils.make() def run_once(self, run_unit_tests=True, run_capture_tests=True, run_default_capture_test=False, time=0, assert_mandatory_controls=False): self.assert_mandatory_controls = assert_mandatory_controls self.find_video_capture_devices() time = time / len(self.v4l2_devices) for device in self.v4l2_devices: if run_unit_tests: self.run_v4l2_unittests(device) if run_capture_tests: self.run_v4l2_capture_tests(device) if run_default_capture_test: self.run_v4l2_default_capture_test(device, time) def is_v4l2_capture_device(self, device): executable = os.path.join(self.bindir, "media_v4l2_is_capture_device") cmd = "%s %s" % (executable, device) logging.info("Running %s" % cmd) return (utils.system(cmd, ignore_status=True) == 0) def find_video_capture_devices(self): self.v4l2_devices = [] for device in glob.glob("/dev/video*"): statinfo = os.stat(device) if (stat.S_ISCHR(statinfo.st_mode) and os.major(statinfo.st_rdev) == self.v4l2_major_dev_num and os.minor(statinfo.st_rdev) >= self.v4l2_minor_dev_num_min and os.minor(statinfo.st_rdev) < self.v4l2_minor_dev_num_max and self.is_v4l2_capture_device(device)): self.v4l2_devices.append(device) logging.info("Detected devices: %s\n" % self.v4l2_devices) if not self.v4l2_devices: raise error.TestFail("No V4L2 devices found!") def unittest_passed(self, testname, stdout): return re.search(r"OK \] V4L2DeviceTest\." + testname, stdout); def run_v4l2_unittests(self, device): self.executable = os.path.join(self.bindir, "media_v4l2_unittest") cmd = "%s --device=%s" % (self.executable, device) logging.info("Running %s" % cmd) stdout = utils.system_output(cmd, retain_output=True) # Check the result of unittests. # We had exercise all the optional ioctls in unittest which maybe # optional by V4L2 Specification. Therefore we need to check those # tests that we thought are mandatory. # 1. Multiple open should be supported for panel application. if not self.unittest_passed("MultipleOpen", stdout): raise error.TestError(device + " does not support multiple open!") # 2. Need to make sure this is really support or just driver error. if not self.unittest_passed("MultipleInit", stdout): raise error.TestError(device + " does support multiple init!") # 3. EnumInput and EnumStandard is optional. # 4. EnumControl is mandatory. if not self.unittest_passed("EnumControl", stdout): raise error.TestError(device + " does support enum controls!") pattern = re.compile(r"Control (\w+) is enabled\((\d+)-(\d+):(\d+)\)") control_info = pattern.findall(stdout) self.supported_controls = [ x[0] for x in control_info ] logging.info("Supported Controls: %s\n" % self.supported_controls) # TODO(jiesun): what is required? mandatory_controls = [ "Brightness", "Contrast", "Saturation", "Hue", "Gamma"] for control in mandatory_controls: if self.assert_mandatory_controls and \ control not in self.supported_controls: raise error.TestError(device + " does not support " + control) # 5. SetControl is mandatory. if not self.unittest_passed("SetControl", stdout): raise error.TestError(device + " does not support set controls!") # 6. 7. Set/GetCrop are both optional. # 8. ProbeCaps is mandatory. if not self.unittest_passed("ProbeCaps", stdout): raise error.TestError(device + " does not support probe caps!") if not re.search(r"support video capture interface.>>>", stdout): raise error.TestFail(device + " does not support video capture!") pattern = r"support streaming i/o interface.>>>" self.support_streaming = True if re.search(pattern, stdout) else False pattern = r"support streaming read/write interface.>>>" self.support_readwrite = True if re.search(pattern, stdout) else False # Currently I assume streaming (mmap) is mandatroy. if not self.support_streaming: raise error.TestFail(device + " does not support streaming!") # 9. EnumFormats is always mandatory. if not self.unittest_passed("EnumFormats", stdout): raise error.TestError(device + " does not support enum formats!") pattern = re.compile(r"supported format #\d+: .* \((....)\)") format_info = pattern.findall(stdout) # Remove duplicated pixel formats from list. self.supported_formats = list(set(format_info)) logging.info("Supported pixel format: %s\n", self.supported_formats) # 10. Get/SetParam for framerate is optional. # 11. EnumFrameSize is optional on some kernel/v4l2 version. def run_v4l2_capture_test(self, fail_okay, options): executable = os.path.join(self.bindir, "media_v4l2_test") try: cmd = "%s %s" % (executable, " ".join(options)) cmd = graphics_utils.xcommand(cmd) logging.info("Running %s" % cmd) stdout = utils.system_output(cmd, retain_output=True) except: if fail_okay: stdout = "" return (False, stdout) else: raise else: return (True, stdout) def run_v4l2_default_capture_test(self, device, time): options = ["--device=%s" % device ] if time: options.append("--time=%d" % time) okay, stdout = self.run_v4l2_capture_test(False, options) def run_v4l2_capture_tests(self, device): default_options = ["--device=%s" % device ] # If the device claims to support read/write i/o. if self.support_readwrite: option = default_options + ["--read"] okay, stdout = self.run_v4l2_capture_test(False, option) # If the device claims to support stream i/o. # This could mean either mmap stream i/o or user pointer stream i/o. if self.support_streaming: option = default_options + ["--mmap"] mmap_okay, stdout = self.run_v4l2_capture_test(True, option) option = default_options + ["--userp"] userp_okay, stdout = self.run_v4l2_capture_test(True, option) if not userp_okay and not mmap_okay: raise error.TestFail("Stream i/o failed!") # TODO(jiesun): test with different mandatory resultions that # the capture device must support without scaling by ourselves. required_resolutions = [ (320, 240, 30), # QVGA (640, 480, 30)] # VGA for (width, height, minfps) in required_resolutions: # Note use default mmap i/o here. option = default_options[:] # Note use first supported pixel format. option.append("--pixel-format=%s" % self.supported_formats[0]) option.append("--width=%s" % width) option.append("--height=%s" % height) okay, stdout = self.run_v4l2_capture_test(False, option) # Check if the actual format is desired. pattern = (r"actual format for capture (\d+)x(\d+)" r" (....) picture at (\d+) fps") match = re.search(pattern, stdout) if (not match or int(match.group(1)) != width or int(match.group(2)) != height or match.group(3) != self.supported_formats[0] or int(match.group(4)) < minfps): raise error.TestError("capture test failed") okay, stdout = self.run_v4l2_capture_test(False, option)