Home | History | Annotate | Download | only in camera_V4L2
      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