aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2014-12-06 11:35:00 +0100
committerHans Verkuil <hans.verkuil@cisco.com>2014-12-06 11:35:00 +0100
commit70f05dfe2afbc2b7d23f0318deb6fba1094595ab (patch)
tree7c04a70959656fb0d2f630d25c31158429a1f04f
parent6df031730148faf76f6ebf3b80590badd0912fda (diff)
v4l2-compliance: add basic crop/compose/scale tests.
These tests are fairly basic, but it's better than nothing. Checking whether a driver supports scaling is way too complex today, this is something that needs to be improved. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
-rw-r--r--utils/v4l2-compliance/v4l2-compliance.cpp5
-rw-r--r--utils/v4l2-compliance/v4l2-compliance.h10
-rw-r--r--utils/v4l2-compliance/v4l2-test-formats.cpp322
3 files changed, 337 insertions, 0 deletions
diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp
index 22226481..be33f2b7 100644
--- a/utils/v4l2-compliance/v4l2-compliance.cpp
+++ b/utils/v4l2-compliance/v4l2-compliance.cpp
@@ -827,6 +827,8 @@ int main(int argc, char **argv)
for (unsigned io = 0; io < (max_io ? max_io : 1); io++) {
node.std_controls = node.priv_controls = 0;
node.controls.clear();
+ node.frmsizes.clear();
+ node.frmsizes_count.clear();
for (unsigned idx = 0; idx < V4L2_BUF_TYPE_SDR_CAPTURE + 1; idx++)
node.buftype_pixfmts[idx].clear();
@@ -873,6 +875,9 @@ int main(int argc, char **argv)
printf("\t\ttest VIDIOC_TRY_FMT: %s\n", ok(testTryFormats(&node)));
printf("\t\ttest VIDIOC_S_FMT: %s\n", ok(testSetFormats(&node)));
printf("\t\ttest VIDIOC_G_SLICED_VBI_CAP: %s\n", ok(testSlicedVBICap(&node)));
+ printf("\t\ttest Cropping: %s\n", ok(testCropping(&node)));
+ printf("\t\ttest Composing: %s\n", ok(testComposing(&node)));
+ printf("\t\ttest Scaling: %s\n", ok(testScaling(&node)));
printf("\n");
/* Codec ioctls */
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
index 569549bf..f4d08f18 100644
--- a/utils/v4l2-compliance/v4l2-compliance.h
+++ b/utils/v4l2-compliance/v4l2-compliance.h
@@ -26,6 +26,7 @@
#include <string>
#include <map>
#include <set>
+#include <map>
#include <linux/videodev2.h>
#ifndef NO_LIBV4L2
@@ -49,6 +50,8 @@ struct test_query_ext_ctrl: v4l2_query_ext_ctrl {
typedef std::map<__u32, struct test_query_ext_ctrl> qctrl_map;
typedef std::set<__u32> pixfmt_set;
+typedef std::set<__u64> frmsizes_set;
+typedef std::map<__u32, unsigned> frmsizes_count_map;
struct base_node;
@@ -61,6 +64,7 @@ struct base_node {
bool is_planar;
bool can_capture;
bool can_output;
+ bool can_scale;
const char *device;
struct node *node2; /* second open filehandle */
bool has_outputs;
@@ -75,6 +79,9 @@ struct base_node {
unsigned std_controls;
unsigned priv_controls;
__u32 fbuf_caps;
+ pixfmt_set buftype_pixfmts[V4L2_BUF_TYPE_SDR_CAPTURE + 1];
+ frmsizes_set frmsizes;
+ frmsizes_count_map frmsizes_count;
__u32 valid_buftypes;
__u32 valid_buftype;
__u32 valid_memorytype;
@@ -179,6 +186,9 @@ int testGetFormats(struct node *node);
int testTryFormats(struct node *node);
int testSetFormats(struct node *node);
int testSlicedVBICap(struct node *node);
+int testCropping(struct node *node);
+int testComposing(struct node *node);
+int testScaling(struct node *node);
// Codec ioctl tests
int testEncoder(struct node *node);
diff --git a/utils/v4l2-compliance/v4l2-test-formats.cpp b/utils/v4l2-compliance/v4l2-test-formats.cpp
index 9cd7b660..c8c545af 100644
--- a/utils/v4l2-compliance/v4l2-test-formats.cpp
+++ b/utils/v4l2-compliance/v4l2-test-formats.cpp
@@ -129,7 +129,9 @@ static int testEnumFrameSizes(struct node *node, __u32 pixfmt)
struct v4l2_frmsizeenum frmsize;
struct v4l2_frmsize_stepwise *sw = &frmsize.stepwise;
bool found_stepwise = false;
+ __u64 cookie;
unsigned f = 0;
+ unsigned count = 0;
int ret;
for (;;) {
@@ -167,6 +169,11 @@ static int testEnumFrameSizes(struct node *node, __u32 pixfmt)
return ret;
if (ret == 0 && !(node->g_caps() & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)))
return fail("found discrete framesizes when no video capture is supported\n");
+ cookie = ((__u64)pixfmt << 32) |
+ (frmsize.discrete.width << 16) |
+ frmsize.discrete.height;
+ node->frmsizes.insert(cookie);
+ count++;
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
if (frmsize.stepwise.step_width != 1 || frmsize.stepwise.step_height != 1)
@@ -206,6 +213,7 @@ static int testEnumFrameSizes(struct node *node, __u32 pixfmt)
f++;
}
+ node->frmsizes_count[pixfmt] = count;
info("found %d framesizes for pixel format %08x\n", f, pixfmt);
return 0;
}
@@ -1129,3 +1137,317 @@ int testParm(struct node *node)
return fail("Buffer type PRIVATE allowed!\n");
return supported ? 0 : ENOTTY;
}
+
+static bool rect_is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+ return r1->left >= r2->left && r1->top >= r2->top &&
+ r1->left + r1->width <= r2->left + r2->width &&
+ r1->top + r1->height <= r2->top + r2->height;
+}
+
+static int testBasicSelection(struct node *node, unsigned type, unsigned target)
+{
+ struct v4l2_selection sel = {
+ type,
+ target,
+ };
+ int ret;
+ v4l2_format fmt;
+
+ memset(sel.reserved, 0xff, sizeof(sel.reserved));
+ ret = doioctl(node, VIDIOC_G_SELECTION, &sel);
+ if (ret == ENOTTY || ret == EINVAL) {
+ fail_on_test(!doioctl(node, VIDIOC_S_SELECTION, &sel));
+ return ENOTTY;
+ }
+ fail_on_test(ret);
+ fail_on_test(check_0(sel.reserved, sizeof(sel.reserved)));
+
+ // selection is not supported (for now) if there is more than one
+ // discrete frame size.
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ v4l_format_init(&fmt, node->is_planar ?
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ else
+ v4l_format_init(&fmt, node->is_planar ?
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ fail_on_test(doioctl(node, VIDIOC_G_FMT, &fmt));
+ __u32 pixfmt = v4l_format_g_pixelformat(&fmt);
+ if (node->frmsizes_count.find(pixfmt) != node->frmsizes_count.end())
+ fail_on_test(node->frmsizes_count[pixfmt] > 1);
+ return 0;
+}
+
+static int testBasicCrop(struct node *node, unsigned type)
+{
+ struct v4l2_selection sel_crop = {
+ type,
+ V4L2_SEL_TGT_CROP,
+ };
+ struct v4l2_selection sel_def;
+ struct v4l2_selection sel_bounds;
+
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel_crop));
+ fail_on_test(!sel_crop.r.width || !sel_crop.r.height);
+ sel_def = sel_crop;
+ sel_def.target = V4L2_SEL_TGT_CROP_DEFAULT;
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel_def));
+ fail_on_test(!sel_def.r.width || !sel_def.r.height);
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ fail_on_test(sel_def.r.left || sel_def.r.top);
+ sel_bounds = sel_crop;
+ sel_bounds.target = V4L2_SEL_TGT_CROP_BOUNDS;
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel_bounds));
+ fail_on_test(!sel_bounds.r.width || !sel_bounds.r.height);
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ fail_on_test(sel_bounds.r.left || sel_bounds.r.top);
+ fail_on_test(!rect_is_inside(&sel_crop.r, &sel_bounds.r));
+ fail_on_test(!rect_is_inside(&sel_def.r, &sel_bounds.r));
+ return 0;
+}
+
+int testCropping(struct node *node)
+{
+ int ret = ENOTTY;
+
+ if (node->can_capture && node->is_video)
+ ret = testBasicSelection(node, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_SEL_TGT_CROP);
+ if (node->can_output && node->is_video)
+ ret = testBasicSelection(node, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_SEL_TGT_CROP);
+ if ((!node->can_capture && !node->can_output) || !node->is_video) {
+ struct v4l2_selection sel = {
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_SEL_TGT_CROP
+ };
+
+ if (node->can_output)
+ sel.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel) != ENOTTY);
+ fail_on_test(doioctl(node, VIDIOC_S_SELECTION, &sel) != ENOTTY);
+ }
+ if (ret)
+ return ret;
+
+ if (node->can_capture)
+ fail_on_test(testBasicCrop(node, V4L2_BUF_TYPE_VIDEO_CAPTURE));
+ if (node->can_output)
+ fail_on_test(testBasicCrop(node, V4L2_BUF_TYPE_VIDEO_OUTPUT));
+ return ret;
+}
+
+static int testBasicCompose(struct node *node, unsigned type)
+{
+ struct v4l2_selection sel_compose = {
+ type,
+ V4L2_SEL_TGT_COMPOSE,
+ };
+ struct v4l2_selection sel_def;
+ struct v4l2_selection sel_bounds;
+ struct v4l2_selection sel_padded;
+ int ret;
+
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel_compose));
+ fail_on_test(!sel_compose.r.width || !sel_compose.r.height);
+ sel_def = sel_compose;
+ sel_def.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel_def));
+ fail_on_test(!sel_def.r.width || !sel_def.r.height);
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ fail_on_test(sel_def.r.left || sel_def.r.top);
+ sel_bounds = sel_compose;
+ sel_bounds.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel_bounds));
+ fail_on_test(!sel_bounds.r.width || !sel_bounds.r.height);
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ fail_on_test(sel_bounds.r.left || sel_bounds.r.top);
+ fail_on_test(!rect_is_inside(&sel_compose.r, &sel_bounds.r));
+ fail_on_test(!rect_is_inside(&sel_def.r, &sel_bounds.r));
+ sel_padded = sel_compose;
+ sel_padded.target = V4L2_SEL_TGT_COMPOSE_PADDED;
+ ret = doioctl(node, VIDIOC_G_SELECTION, &sel_padded);
+ fail_on_test(ret && ret != EINVAL);
+ if (!ret) {
+ fail_on_test(!rect_is_inside(&sel_padded.r, &sel_bounds.r));
+ fail_on_test(!sel_padded.r.width || !sel_padded.r.height);
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ fail_on_test(sel_padded.r.left || sel_padded.r.top);
+ }
+ return 0;
+}
+
+int testComposing(struct node *node)
+{
+ int ret = ENOTTY;
+
+ if (node->can_capture && node->is_video)
+ ret = testBasicSelection(node, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_SEL_TGT_COMPOSE);
+ if (node->can_output && node->is_video)
+ ret = testBasicSelection(node, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_SEL_TGT_COMPOSE);
+ if ((!node->can_capture && !node->can_output) || !node->is_video) {
+ struct v4l2_selection sel = {
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_SEL_TGT_COMPOSE
+ };
+
+ if (node->can_output)
+ sel.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ fail_on_test(doioctl(node, VIDIOC_G_SELECTION, &sel) != ENOTTY);
+ fail_on_test(doioctl(node, VIDIOC_S_SELECTION, &sel) != ENOTTY);
+ }
+ if (ret)
+ return ret;
+
+ if (node->can_capture)
+ fail_on_test(testBasicCompose(node, V4L2_BUF_TYPE_VIDEO_CAPTURE));
+ if (node->can_output)
+ fail_on_test(testBasicCompose(node, V4L2_BUF_TYPE_VIDEO_OUTPUT));
+ return ret;
+}
+
+static int testBasicScaling(struct node *node, const struct v4l2_format &cur)
+{
+ struct v4l2_selection sel_crop = {
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_SEL_TGT_CROP,
+ 0,
+ { 1, 1, 0, 0 }
+ };
+ struct v4l2_selection sel_compose = {
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_SEL_TGT_COMPOSE,
+ 0,
+ { 1, 1, 0, 0 }
+ };
+ unsigned compose_w = 0, compose_h = 0;
+ unsigned crop_w = 0, crop_h = 0;
+ struct v4l2_format fmt = cur;
+ bool have_crop = false;
+ bool crop_is_fmt = false;
+ bool crop_is_const = false;
+ bool have_compose = false;
+ bool compose_is_fmt = false;
+ bool compose_is_const = false;
+ bool compose_is_crop = false;
+ __u64 cookie;
+ int ret;
+
+ if (node->can_output) {
+ sel_crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ sel_compose.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ }
+ doioctl(node, VIDIOC_S_SELECTION, &sel_crop);
+ doioctl(node, VIDIOC_S_SELECTION, &sel_compose);
+ v4l_format_s_width(&fmt, 1);
+ v4l_format_s_height(&fmt, 1);
+ v4l_format_s_field(&fmt, V4L2_FIELD_ANY);
+ fail_on_test(doioctl(node, VIDIOC_S_FMT, &fmt));
+ ret = doioctl(node, VIDIOC_G_SELECTION, &sel_crop);
+ have_crop = ret == 0;
+ if (ret == 0) {
+ crop_is_fmt = sel_crop.r.width == v4l_format_g_width(&fmt) &&
+ sel_crop.r.height == v4l_format_g_height(&fmt);
+ crop_w = sel_crop.r.width;
+ crop_h = sel_crop.r.height;
+ }
+ ret = doioctl(node, VIDIOC_G_SELECTION, &sel_compose);
+ have_compose = ret == 0;
+ if (ret == 0) {
+ compose_is_fmt = sel_compose.r.width == v4l_format_g_width(&fmt) &&
+ sel_compose.r.height == v4l_format_g_height(&fmt);
+ compose_w = sel_compose.r.width;
+ compose_h = sel_compose.r.height;
+ }
+ if (have_crop && have_compose)
+ compose_is_crop = compose_w == crop_w &&
+ compose_h == crop_h;
+
+ cookie = ((__u64)v4l_format_g_pixelformat(&fmt) << 32) |
+ (v4l_format_g_width(&fmt) << 16) |
+ v4l_format_g_height(&fmt);
+ if (node->can_output) {
+ if (node->frmsizes.find(cookie) == node->frmsizes.end() && !compose_is_fmt &&
+ (v4l_format_g_width(&fmt) != v4l_format_g_width(&cur) ||
+ v4l_format_g_height(&fmt) != v4l_format_g_height(&cur)))
+ node->can_scale = true;
+ } else {
+ if (node->frmsizes.find(cookie) == node->frmsizes.end() && !crop_is_fmt &&
+ (v4l_format_g_width(&fmt) != v4l_format_g_width(&cur) ||
+ v4l_format_g_height(&fmt) != v4l_format_g_height(&cur)))
+ node->can_scale = true;
+ }
+ sel_crop.r.width = sel_compose.r.width = 0x4000;
+ sel_crop.r.height = sel_compose.r.height = 0x4000;
+ v4l_format_s_width(&fmt, 0x4000);
+ v4l_format_s_height(&fmt, 0x4000);
+ v4l_format_s_field(&fmt, V4L2_FIELD_ANY);
+ fail_on_test(doioctl(node, VIDIOC_S_FMT, &fmt));
+ doioctl(node, VIDIOC_S_SELECTION, &sel_crop);
+ doioctl(node, VIDIOC_S_SELECTION, &sel_compose);
+ crop_is_fmt = false;
+ ret = doioctl(node, VIDIOC_G_SELECTION, &sel_crop);
+ if (ret == 0) {
+ crop_is_fmt = sel_crop.r.width == v4l_format_g_width(&fmt) &&
+ sel_crop.r.height == v4l_format_g_height(&fmt);
+ crop_is_const = sel_crop.r.width == crop_w &&
+ sel_crop.r.height == crop_h;
+ }
+ ret = doioctl(node, VIDIOC_G_SELECTION, &sel_compose);
+ if (ret == 0) {
+ compose_is_fmt = sel_compose.r.width == v4l_format_g_width(&fmt) &&
+ sel_compose.r.height == v4l_format_g_height(&fmt);
+ compose_is_const = sel_compose.r.width == compose_w &&
+ sel_compose.r.height == compose_h;
+ }
+ if (compose_is_crop)
+ compose_is_crop = sel_compose.r.width == sel_crop.r.width &&
+ sel_compose.r.height == sel_crop.r.height;
+ cookie = ((__u64)v4l_format_g_pixelformat(&fmt) << 32) |
+ (v4l_format_g_width(&fmt) << 16) |
+ v4l_format_g_height(&fmt);
+ if (node->can_output) {
+ if (node->frmsizes.find(cookie) == node->frmsizes.end() && !compose_is_fmt &&
+ (v4l_format_g_width(&fmt) != v4l_format_g_width(&cur) ||
+ v4l_format_g_height(&fmt) != v4l_format_g_height(&cur)))
+ node->can_scale = true;
+ if (crop_is_const || compose_is_crop)
+ node->can_scale = false;
+ } else {
+ if (node->frmsizes.find(cookie) == node->frmsizes.end() && !crop_is_fmt &&
+ (v4l_format_g_width(&fmt) != v4l_format_g_width(&cur) ||
+ v4l_format_g_height(&fmt) != v4l_format_g_height(&cur)))
+ node->can_scale = true;
+ if (compose_is_const || compose_is_crop)
+ node->can_scale = false;
+ }
+ fail_on_test(node->can_scale &&
+ node->frmsizes_count[v4l_format_g_pixelformat(&cur)]);
+ return 0;
+}
+
+int testScaling(struct node *node)
+{
+ struct v4l2_format fmt;
+
+ if (!node->is_video)
+ return ENOTTY;
+ node->can_scale = false;
+ if (node->can_capture) {
+ v4l_format_init(&fmt, node->is_planar ?
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ fail_on_test(doioctl(node, VIDIOC_G_FMT, &fmt));
+ testBasicScaling(node, fmt);
+ fail_on_test(doioctl(node, VIDIOC_S_FMT, &fmt));
+ }
+ if (node->can_output) {
+ v4l_format_init(&fmt, node->is_planar ?
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ fail_on_test(doioctl(node, VIDIOC_G_FMT, &fmt));
+ testBasicScaling(node, fmt);
+ fail_on_test(doioctl(node, VIDIOC_S_FMT, &fmt));
+ }
+ return node->can_scale ? 0 : ENOTTY;
+}

Privacy Policy