• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import glob, logging, os, re, stat
6from autotest_lib.client.bin import test, utils
7from autotest_lib.client.common_lib import error
8from autotest_lib.client.cros.graphics import graphics_utils
9
10
11class 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