Add the hardware_VideoDecodeCapable tests.

The new tests is also includes the original "audiovideo_Formats"
which only performs the tests on daisy.

In this new test, we add the support for the platoforms uses VAAPI
for video decoding.

BUG=chromium:218145
TEST=build and run the test on link and daisy

Change-Id: I5566289ecdeda999c8ab9d7b7f86e8b7316e023d
Reviewed-on: https://gerrit.chromium.org/gerrit/47985
Reviewed-by: Pawel Osciak <posciak@chromium.org>
Commit-Queue: Owen Lin <owenlin@chromium.org>
Tested-by: Owen Lin <owenlin@chromium.org>
diff --git a/client/site_tests/hardware_VideoDecodeCapable/control.daisy b/client/site_tests/hardware_VideoDecodeCapable/control.daisy
new file mode 100644
index 0000000..216e148
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/control.daisy
@@ -0,0 +1,29 @@
+# Copyright (c) 2013 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.
+
+AUTHOR = 'owenlin@chromium.org, chromeos-video@google.com'
+NAME = 'hardware_VideoDecodeCapable'
+PURPOSE = 'Verify formats availability for hardware offload.'
+CRITERIA = """
+This test will fail if reported supported formats are different from expected.
+For example, daisy should support hardware VP8 and H264 decode.
+
+This control file for Daisy/Snow.
+"""
+SUITE = 'bvt'
+TIME='FAST'
+TEST_CATEGORY = 'Functional'
+TEST_CLASS = 'hardware'
+TEST_TYPE = 'client'
+DEPENDENCIES = 'board:daisy'
+EXPERIMENTAL = 'True'
+
+DOC = """
+V4L2 formats enumeration is done in C. It is built as a extension module
+(v4l2.so) during setup.
+"""
+
+DAISY_VIDEO_DEC = "/dev/mfc-dec"
+
+job.run_test('hardware_VideoDecodeCapable', video_device=DAISY_VIDEO_DEC)
diff --git a/client/site_tests/hardware_VideoDecodeCapable/control.vaapi b/client/site_tests/hardware_VideoDecodeCapable/control.vaapi
new file mode 100644
index 0000000..e8e604b
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/control.vaapi
@@ -0,0 +1,30 @@
+# Copyright (c) 2013 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.
+
+AUTHOR = 'The Chromium OS Authors,chromeos-video@google.com'
+NAME = 'hardware_VideoDecodeCapable'
+PURPOSE = 'Probe for the video decoding capability'
+CRITERIA = """
+This test fails when the video decoding capabilities are different from expected.
+
+This control file is for platforms supporting VAAPI. We expect the following
+Profiles are supported: H264_BASELINE, H264_MAIN, and H264_HIGH
+
+For each of the profiles, we expect it has the VLD entrypoint and supports
+YUV420 for the RT format.
+"""
+SUITE = 'bvt'
+TIME='FAST'
+TEST_CATEGORY = 'Functional'
+TEST_CLASS = 'hardware'
+TEST_TYPE = 'client'
+DEPENDENCIES = 'vaapi'
+EXPERIMENTAL = 'True'
+
+DOC = """
+VAAPI capabilities probing is done in C. It is built as an extension module
+(vaapi.so) during setup.
+"""
+
+job.run_test('hardware_VideoDecodeCapable', type='vaapi')
diff --git a/client/site_tests/hardware_VideoDecodeCapable/hardware_VideoDecodeCapable.py b/client/site_tests/hardware_VideoDecodeCapable/hardware_VideoDecodeCapable.py
new file mode 100644
index 0000000..e639e0f
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/hardware_VideoDecodeCapable.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2013 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.
+
+"""Test to probe the video capability."""
+import logging, os, sys
+from autotest_lib.client.bin import test, utils
+from autotest_lib.client.common_lib import error
+
+# The following VA_XXX values are copied from va/va.h
+# VA profiles that we are interested in
+VAProfileH264Baseline = 5
+VAProfileH264Main = 6
+VAProfileH264High = 7
+
+# VA Entrypoint that we are interested in
+VAEntrypointVLD = 1
+
+# VA_RT_FORMAT that we are interested in
+VA_RT_FORMAT_YUV420 = 0x01
+
+class hardware_VideoDecodeCapable(test.test):
+    """Test class to verify hardware video decoding capability."""
+
+    version = 1
+
+    REQUESTED_VAAPI_PROFILES = [
+        VAProfileH264Baseline,
+        VAProfileH264Main,
+        VAProfileH264High]
+
+    REQUESTED_V4L2_FORMATS = [
+        'cap_fmt_VM12',
+        'cap_fmt_NM12',
+        'out_fmt_H264',
+        'out_fmt_VP80']
+
+    def assertTrue(self, condition, message = '', *args):
+        """Raises an TestFail when the assertion failed"""
+        if (not condition):
+            raise error.TestFail(message % args)
+
+
+    def verifyProfile(self, vaapi, display, profile):
+        """Verifies the given profile satisfies the requirements.
+
+        1. It has the VLD entrypoint
+        2. It supports  YUV420 for RT_FORMAT
+
+        @param vaapi: the vaapi module
+
+        @param display: the va_display instance
+
+        @param profile: the profile under test
+
+        @raise error.TestFail: when verification fails
+        """
+        entrypoints = vaapi.query_entrypoints(display, profile)
+        logging.info('Entrypoints of profile %s: %s', profile, entrypoints)
+        self.assertTrue(VAEntrypointVLD in entrypoints,
+                        'VAEntrypointVLD is not supported')
+
+        rt_format = vaapi.get_rt_format(display, profile, VAEntrypointVLD)
+        logging.info('RT_Format: %s', rt_format)
+        self.assertTrue(VA_RT_FORMAT_YUV420 & rt_format,
+                        'VA_RT_FORMAT_YUV420 is not supported');
+
+
+    def setup(self):
+        os.chdir(self.srcdir)
+
+        # Ignores the fail status since vaapi module won't get built on
+        # platforms without VAAPI support (e.g., Daisy). On those platforms
+        # we will test with the v4l2 module.
+        utils.make('v4l2', ignore_status = True)
+        utils.make('vaapi', ignore_status = True)
+
+
+    def run_once_vaapi(self):
+        sys.path.append(self.bindir)
+        import vaapi
+
+        # Set the XAUTHORITY for connecting to the X server
+        os.environ.setdefault('XAUTHORITY', '/home/chronos/.Xauthority')
+
+        display = vaapi.create_display(':0.0')
+        supported_profiles = vaapi.query_profiles(display);
+        logging.info('Vaapi Profiles: %s', supported_profiles);
+
+        for profile in self.REQUESTED_VAAPI_PROFILES:
+            self.assertTrue(profile in supported_profiles,
+                            'Profile:%s is not supported',
+                            profile)
+            self.verifyProfile(vaapi, display, profile)
+
+
+    def _enum_formats(self, video_device):
+        """Use the v4l2 binary to enum formats.
+
+        Runs the embedded v4l2 binary to enumerate supported
+        capture formats and output formats.
+
+        @param video_device: device interrogated (e.g. /dev/mfc-dec).
+
+        @return a dict of keyvals reflecting the formats supported.
+        """
+        sys.path.append(self.bindir)
+        import v4l2
+
+        capture_formats = v4l2.enum_capture_formats(video_device)
+        logging.info('Capture formats=%s', capture_formats)
+        output_formats = v4l2.enum_output_formats(video_device)
+        logging.info('Output formats=%s', output_formats)
+
+        return (['cap_fmt_%s' % fmt for fmt in capture_formats] +
+                ['out_fmt_%s' % fmt for fmt in output_formats])
+
+
+    def run_once_v4l2(self, video_device=None):
+        """Emit supported image formats as keyvals.
+
+        @param video_device: used by v4l2 binary to enum formats.
+        """
+        if not video_device:
+            raise error.TestError('no video_device supplied to check')
+        missed = (set(self.REQUESTED_V4L2_FORMATS) -
+                  set(self._enum_formats(video_device)))
+        self.assertTrue(not missed, 'Formats: %s is not supported', missed)
+
+
+    def run_once(self, type='v4l2', **kwargs):
+        if type == 'v4l2':
+            video_device = kwargs['video_device']
+            self.run_once_v4l2(video_device = video_device)
+        else:
+            self.run_once_vaapi()
+
diff --git a/client/site_tests/hardware_VideoDecodeCapable/src/Makefile b/client/site_tests/hardware_VideoDecodeCapable/src/Makefile
new file mode 100644
index 0000000..ae836d8
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/src/Makefile
@@ -0,0 +1,11 @@
+# Copyright (c) 2013 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.
+
+.PHONY:v4l2, vaapi
+
+v4l2:
+	python setup_v4l2.py install --install-lib=..
+
+vaapi:
+	python setup_vaapi.py install --install-lib=..
diff --git a/client/site_tests/hardware_VideoDecodeCapable/src/setup_v4l2.py b/client/site_tests/hardware_VideoDecodeCapable/src/setup_v4l2.py
new file mode 100644
index 0000000..3820a77
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/src/setup_v4l2.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2013 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.
+
+"""Script to set up v4l2 extension module.
+"""
+from distutils.core import setup, Extension
+
+setup (name = 'v4l2',
+       version = '1.0',
+       ext_modules = [Extension('v4l2', ['v4l2module.cc'])])
diff --git a/client/site_tests/hardware_VideoDecodeCapable/src/setup_vaapi.py b/client/site_tests/hardware_VideoDecodeCapable/src/setup_vaapi.py
new file mode 100644
index 0000000..fb7368c
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/src/setup_vaapi.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2013 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.
+
+"""Script to set up vaapi extension module.
+"""
+from distutils.core import setup, Extension
+
+module = Extension('vaapi',
+                   libraries = ['X11', 'va', 'va-x11'],
+                   sources = ['vaapimodule.cc'])
+
+setup(name = 'vaapi', version = '1.0', ext_modules = [module])
diff --git a/client/site_tests/hardware_VideoDecodeCapable/src/v4l2module.cc b/client/site_tests/hardware_VideoDecodeCapable/src/v4l2module.cc
new file mode 100644
index 0000000..e08d6f9
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/src/v4l2module.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2013 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.
+
+#include <Python.h>
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+
+static int do_ioctl(int fd, int request, void* arg) {
+  int r;
+  do {
+    r = ioctl(fd, request, arg);
+  } while (-1 == r && EINTR == errno);
+  return r;
+}
+
+static void v4l2_enum_formats(const char *dev, int buf_type,
+                              PyObject *formats) {
+  int fd = open(dev, O_RDWR | O_NONBLOCK, 0);
+  if (fd == -1) return;
+
+  for (int i = 0; ; ++i) {
+    char pixel_format[4];
+    v4l2_fmtdesc format_desc;
+
+    memset(&format_desc, 0, sizeof(format_desc));
+    format_desc.type = (v4l2_buf_type) buf_type;
+    format_desc.index = i;
+    if (-1 == do_ioctl(fd, VIDIOC_ENUM_FMT, &format_desc)) {
+      break;
+    }
+    pixel_format[0] = format_desc.pixelformat & 0xFF;
+    pixel_format[1] = (format_desc.pixelformat >> 8) & 0xFF;
+    pixel_format[2] = (format_desc.pixelformat >> 16) & 0xFF;
+    pixel_format[3] = (format_desc.pixelformat >> 24) & 0xFF;
+    PyObject* item = PyString_FromStringAndSize(pixel_format, 4);
+    PyList_Append(formats, item);
+    Py_DECREF(item);
+  }
+  close(fd);
+}
+
+static PyObject *v4l2_enum_capture_formats(PyObject *self, PyObject *args) {
+  const char *dev;
+  if (!PyArg_ParseTuple(args, "s", &dev))
+    return NULL;
+  PyObject *formats = PyList_New(0);
+  v4l2_enum_formats(dev, V4L2_BUF_TYPE_VIDEO_CAPTURE, formats);
+  v4l2_enum_formats(dev, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, formats);
+  return formats;
+}
+
+static PyObject *v4l2_enum_output_formats(PyObject *self, PyObject *args) {
+  const char *dev;
+  if (!PyArg_ParseTuple(args, "s", &dev))
+    return NULL;
+  PyObject *formats = PyList_New(0);
+  v4l2_enum_formats(dev, V4L2_BUF_TYPE_VIDEO_OUTPUT, formats);
+  v4l2_enum_formats(dev, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, formats);
+  return formats;
+}
+
+/*
+ * Bind Python function names to our C functions
+ */
+static PyMethodDef v4l2_methods[] = {
+    {"enum_capture_formats", v4l2_enum_capture_formats, METH_VARARGS},
+    {"enum_output_formats", v4l2_enum_output_formats, METH_VARARGS},
+    {NULL, NULL}
+};
+
+/*
+ * Python calls this to let us initialize our module
+ */
+PyMODINIT_FUNC initv4l2() {
+  (void) Py_InitModule("v4l2", v4l2_methods);
+}
diff --git a/client/site_tests/hardware_VideoDecodeCapable/src/vaapimodule.cc b/client/site_tests/hardware_VideoDecodeCapable/src/vaapimodule.cc
new file mode 100644
index 0000000..324fe6e
--- /dev/null
+++ b/client/site_tests/hardware_VideoDecodeCapable/src/vaapimodule.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2013 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.
+
+#include <Python.h>
+#include <X11/Xlib.h>
+#include <va/va.h>
+#include <va/va_x11.h>
+
+static PyObject *VaapiError;
+
+namespace {
+
+struct DisplayBundle {
+  Display *x11_display;
+  VADisplay va_display;
+};
+
+static void destroy_display_bundle(PyObject* object) {
+  DisplayBundle* bundle = (DisplayBundle*) PyCapsule_GetPointer(object, NULL);
+  vaTerminate(bundle->va_display);
+  XCloseDisplay(bundle->x11_display);
+  delete bundle;
+}
+
+static PyObject* va_create_display(PyObject* self, PyObject* args) {
+  const char* display_name;
+  if (!PyArg_ParseTuple(args, "s", &display_name))
+    return NULL;
+
+  Display *x11_display = XOpenDisplay(display_name);
+
+  if (x11_display == NULL) {
+    PyErr_SetString(VaapiError, "Cannot connect X server!");
+    return NULL;
+  }
+
+  VADisplay va_display = vaGetDisplay(x11_display);
+  if (!vaDisplayIsValid(va_display)) {
+    PyErr_SetString(VaapiError, "Cannot get a valid display");
+    return NULL;
+  }
+
+  int major_ver, minor_ver;
+
+  VAStatus va_status = vaInitialize(va_display, &major_ver, &minor_ver);
+  if (va_status != VA_STATUS_SUCCESS) {
+    PyErr_SetString(VaapiError, "vaInitialize fail");
+    return NULL;
+  }
+
+  DisplayBundle* bundle = new DisplayBundle();
+  bundle->x11_display = x11_display;
+  bundle->va_display = va_display;
+
+  return PyCapsule_New(bundle, NULL, destroy_display_bundle);
+}
+
+static VADisplay get_va_display(PyObject* object) {
+  if (!PyCapsule_CheckExact(object)) {
+    PyErr_SetString(VaapiError, "invalid display object");
+    return NULL;
+  }
+
+  DisplayBundle* bundle = (DisplayBundle*) PyCapsule_GetPointer(object, NULL);
+
+  if (bundle == NULL)
+    return NULL;
+
+  return bundle->va_display;
+}
+
+static PyObject* va_query_profiles(PyObject* self, PyObject* args) {
+  PyObject* bundle;
+  if (!PyArg_ParseTuple(args, "O", &bundle))
+    return NULL;
+
+  VADisplay va_display = get_va_display(bundle);
+
+  if (va_display == NULL)
+    return NULL;
+
+  int num_profiles = vaMaxNumProfiles(va_display);
+  VAProfile *profile = new VAProfile[num_profiles];
+
+  VAStatus status = vaQueryConfigProfiles(va_display, profile, &num_profiles);
+
+  if (status != VA_STATUS_SUCCESS) {
+    delete [] profile;
+    PyErr_SetString(VaapiError, "vaQueryConfigProfiles fail");
+    return NULL;
+  }
+
+  PyObject *result = PyList_New(0);
+  for (int i = 0; i < num_profiles; ++i) {
+    size_t value = static_cast<size_t>(profile[i]);
+    PyList_Append(result, PyInt_FromSize_t(value));
+  }
+  delete [] profile;
+  return result;
+}
+
+static PyObject* va_query_entrypoints(PyObject* self, PyObject* args) {
+  PyObject* bundle;
+  int profile;
+  if (!PyArg_ParseTuple(args, "Oi", &bundle, &profile))
+    return NULL;
+
+  VADisplay va_display = get_va_display(bundle);
+  if (va_display == NULL) 
+    return NULL;
+
+  int num_entrypoints = vaMaxNumEntrypoints(va_display);
+  VAEntrypoint* entrypoint = new VAEntrypoint[num_entrypoints];
+
+  VAStatus status = vaQueryConfigEntrypoints(va_display,
+                                             static_cast<VAProfile>(profile),
+                                             entrypoint,
+                                             &num_entrypoints);
+  if (status != VA_STATUS_SUCCESS) {
+    PyErr_SetString(VaapiError, "vaQueryConfigEntryPoints fail");
+    return NULL;
+  }
+
+  PyObject *result = PyList_New(0);
+  for (int i = 0; i < num_entrypoints; ++i) {
+    size_t value = static_cast<size_t>(entrypoint[i]);
+    PyList_Append(result, PyInt_FromSize_t(value));
+  }
+  return result;
+}
+
+static PyObject* va_get_rt_format(PyObject* self, PyObject* args) {
+  PyObject* bundle;
+  int profile;
+  int entrypoint;
+  if (!PyArg_ParseTuple(args, "Oii", &bundle, &profile, &entrypoint))
+    return NULL;
+
+  VADisplay va_display = get_va_display(bundle);
+  if (va_display == NULL) 
+    return NULL;
+
+  VAConfigAttrib attrib;
+  attrib.type = VAConfigAttribRTFormat;
+  VAStatus status = vaGetConfigAttributes(va_display,
+                                          static_cast<VAProfile>(profile),
+                                          static_cast<VAEntrypoint>(entrypoint),
+                                          &attrib,
+                                          1);
+  if (status != VA_STATUS_SUCCESS) {
+    PyErr_SetString(VaapiError, "vaGetConfgAttributes fail");
+    return NULL;
+  }
+
+  return PyInt_FromSize_t(attrib.value);
+}
+
+/*
+ * Bind Python function names to our C functions
+ */
+static PyMethodDef vaapi_methods[] = {
+    {"create_display", va_create_display, METH_VARARGS},
+    {"query_profiles", va_query_profiles, METH_VARARGS},
+    {"query_entrypoints", va_query_entrypoints, METH_VARARGS},
+    {"get_rt_format", va_get_rt_format, METH_VARARGS},
+    {NULL, NULL}
+};
+
+} // end of namespace
+
+/*
+ * Python calls this to let us initialize our module
+ */
+PyMODINIT_FUNC initvaapi() {
+  PyObject *m = Py_InitModule("vaapi", vaapi_methods);
+  if (m == NULL)
+    return;
+
+  VaapiError = PyErr_NewException("vaapi.error", NULL, NULL);
+  Py_INCREF(VaapiError);
+  PyModule_AddObject(m, "error", VaapiError);
+}