blob: 45979f508758db2178b6efdf5ce6e270bf5818f8 [file] [log] [blame]
# 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, time
from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import cros_ui
class audiovideo_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 = cros_ui.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 ]
options.append("--display")
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
(352, 288, 30), # CIF
(640, 480, 30), # VGA
(176, 144, 30)] # QCIF
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")
# Run the test again with X display.
option.append("--display")
okay, stdout = self.run_v4l2_capture_test(False, option)