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);
+}