From b339d4804d10d3e5853c67eadab7f3210fe8e69c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jun 2011 11:36:09 +0200 Subject: [PATCH] v4l2-compliance: add VIDIOC_G_FMT compliance tests. Signed-off-by: Hans Verkuil --- utils/v4l2-compliance/v4l2-compliance.cpp | 34 ++- utils/v4l2-compliance/v4l2-compliance.h | 16 ++ utils/v4l2-compliance/v4l2-test-formats.cpp | 266 ++++++++++++++------ utils/v4l2-ctl/v4l2-ctl.cpp | 2 + 4 files changed, 245 insertions(+), 73 deletions(-) diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp index c06844e6..b7116359 100644 --- a/utils/v4l2-compliance/v4l2-compliance.cpp +++ b/utils/v4l2-compliance/v4l2-compliance.cpp @@ -159,6 +159,36 @@ std::string cap2s(unsigned cap) return s; } +std::string buftype2s(int type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return "Video Capture"; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return "Video Capture Multiplanar"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return "Video Output"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return "Video Output Multiplanar"; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return "Video Overlay"; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return "VBI Capture"; + case V4L2_BUF_TYPE_VBI_OUTPUT: + return "VBI Output"; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return "Sliced VBI Capture"; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return "Sliced VBI Output"; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + return "Video Output Overlay"; + case V4L2_BUF_TYPE_PRIVATE: + return "Private"; + default: + return std::string("Unknown"); + } +} + const char *ok(int res) { static char buf[100]; @@ -417,6 +447,7 @@ int main(int argc, char **argv) node.fd = radio_node.fd; device = radio_device; node.is_radio = true; + printf("is radio\n"); } else if (vbi_node.fd >= 0) { node.fd = vbi_node.fd; device = vbi_device; @@ -551,12 +582,13 @@ int main(int argc, char **argv) printf("Format ioctls:\n"); printf("\ttest VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: %s\n", ok(testEnumFormats(&node))); + printf("\ttest VIDIOC_G_FMT: %s\n", ok(testFormats(&node))); /* TODO: VIDIOC_CROPCAP, VIDIOC_G/S_CROP VIDIOC_G/S_FBUF/OVERLAY - VIDIOC_G/S/TRY_FMT + VIDIOC_S/TRY_FMT VIDIOC_G/S_PARM VIDIOC_G/S_JPEGCOMP VIDIOC_SLICED_VBI_CAP diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h index e17bcc17..dcd0aec8 100644 --- a/utils/v4l2-compliance/v4l2-compliance.h +++ b/utils/v4l2-compliance/v4l2-compliance.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -37,6 +38,7 @@ struct test_queryctrl: v4l2_queryctrl { }; typedef std::list qctrl_list; +typedef std::set<__u32> pixfmt_set; struct node { int fd; @@ -55,6 +57,7 @@ struct node { unsigned std_controls; unsigned priv_controls; qctrl_list controls; + pixfmt_set buftype_pixfmts[V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE + 1]; }; #define info(fmt, args...) \ @@ -76,6 +79,12 @@ struct node { 1; \ }) +#define fail_on_test(test) \ + do { \ + if (test) \ + return fail("%s(%d): %s\n", __FILE__, __LINE__, #test); \ + } while (0) + static inline int test_open(const char *file, int oflag) { return wrapper ? v4l2_open(file, oflag) : open(file, oflag); @@ -107,6 +116,12 @@ int doioctl_name(struct node *node, unsigned long int request, void *parm, const #define doioctl(n, r, p) doioctl_name(n, r, p, #r) std::string cap2s(unsigned cap); +std::string buftype2s(int type); +static inline std::string buftype2s(enum v4l2_buf_type type) +{ + return buftype2s((int)type); +} + const char *ok(int res); int check_string(const char *s, size_t len); int check_ustring(const __u8 *s, int len); @@ -143,5 +158,6 @@ int testCustomTimings(struct node *node); // Format ioctl tests int testEnumFormats(struct node *node); +int testFormats(struct node *node); #endif diff --git a/utils/v4l2-compliance/v4l2-test-formats.cpp b/utils/v4l2-compliance/v4l2-test-formats.cpp index 852a0972..6fc23e2b 100644 --- a/utils/v4l2-compliance/v4l2-test-formats.cpp +++ b/utils/v4l2-compliance/v4l2-test-formats.cpp @@ -29,8 +29,23 @@ #include #include #include +#include #include "v4l2-compliance.h" +static const __u32 buftype2cap[] = { + 0, + V4L2_CAP_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_OUTPUT, + V4L2_CAP_VIDEO_OVERLAY, + V4L2_CAP_VBI_CAPTURE, + V4L2_CAP_VBI_OUTPUT, + V4L2_CAP_SLICED_VBI_CAPTURE, + V4L2_CAP_SLICED_VBI_OUTPUT, + V4L2_CAP_VIDEO_OUTPUT_OVERLAY, + // To be discussed + V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_OUTPUT, +}; static int testEnumFrameIntervals(struct node *node, __u32 pixfmt, __u32 w, __u32 h, bool valid) { @@ -185,6 +200,7 @@ static int testEnumFrameSizes(struct node *node, __u32 pixfmt) static int testEnumFormatsType(struct node *node, enum v4l2_buf_type type) { + pixfmt_set &set = node->buftype_pixfmts[type]; struct v4l2_fmtdesc fmtdesc; unsigned f = 0; int ret; @@ -206,6 +222,8 @@ static int testEnumFormatsType(struct node *node, enum v4l2_buf_type type) return fail("fmtdesc.reserved not zeroed\n"); if (fmtdesc.index != f) return fail("fmtdesc.index was modified\n"); + if (fmtdesc.type != type) + return fail("fmtdesc.type was modified\n"); ret = check_ustring(fmtdesc.description, sizeof(fmtdesc.description)); if (ret) return fail("fmtdesc.description not set\n"); @@ -221,6 +239,13 @@ static int testEnumFormatsType(struct node *node, enum v4l2_buf_type type) if (ret == 0 && !(node->caps & (V4L2_CAP_VIDEO_CAPTURE | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))) return fail("found framesizes when no video capture is supported\n"); f++; + if (type == V4L2_BUF_TYPE_PRIVATE) + continue; + // Update array in v4l2-compliance.h if new buffer types are added + assert(type <= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (set.find(fmtdesc.pixelformat) != set.end()) + return fail("duplicate format %08x\n", fmtdesc.pixelformat); + set.insert(fmtdesc.pixelformat); } info("found %d formats for buftype %d\n", f, type); return 0; @@ -228,93 +253,190 @@ static int testEnumFormatsType(struct node *node, enum v4l2_buf_type type) int testEnumFormats(struct node *node) { + static const __u32 buftype2cap[] = { + 0, + V4L2_CAP_VIDEO_CAPTURE, + V4L2_CAP_VIDEO_OUTPUT, + V4L2_CAP_VIDEO_OVERLAY, + V4L2_CAP_VBI_CAPTURE, + V4L2_CAP_VBI_OUTPUT, + V4L2_CAP_SLICED_VBI_CAPTURE, + V4L2_CAP_SLICED_VBI_OUTPUT, + V4L2_CAP_VIDEO_OUTPUT_OVERLAY, + V4L2_CAP_VIDEO_CAPTURE_MPLANE, + V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }; + int type; int ret; - ret = testEnumFormatsType(node, (enum v4l2_buf_type)0); - if (ret != -ENOSYS) - return fail("Buffer type 0 accepted!\n"); - - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (ret > 0) - return ret; - if (ret && (node->caps & V4L2_CAP_VIDEO_CAPTURE)) - return fail("Video capture cap set, but no capture formats defined\n"); - if (!ret && !(node->caps & V4L2_CAP_VIDEO_CAPTURE)) - return fail("Video capture cap not set, but capture formats defined\n"); - - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (ret > 0) - return ret; - if (ret && (node->caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) - return fail("MPlane Video capture cap set, but no multiplanar capture formats defined\n"); - if (!ret && !(node->caps & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) - return fail("MPlane Video capture cap not set, but multiplanar capture formats defined\n"); - - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VIDEO_OVERLAY); - if (ret > 0) - return ret; - if (ret && (node->caps & V4L2_CAP_VIDEO_OVERLAY)) - return fail("Video overlay cap set, but no overlay formats defined\n"); - if (!ret && !(node->caps & V4L2_CAP_VIDEO_OVERLAY)) - return fail("Video overlay cap not set, but overlay formats defined\n"); - - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VBI_CAPTURE); - if (ret > 0) - return ret; - if (!ret) - return fail("Buffer type VBI_CAPTURE allowed!\n"); + for (type = 0; type <= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; type++) { + ret = testEnumFormatsType(node, (enum v4l2_buf_type)type); + if (ret > 0) + return ret; + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (ret < 0 && (node->caps & buftype2cap[type])) + return fail("%s cap set, but no %s formats defined\n", + buftype2s(type).c_str(), buftype2s(type).c_str()); + if (!ret && !(node->caps & buftype2cap[type])) + return fail("%s cap not set, but %s formats defined\n", + buftype2s(type).c_str(), buftype2s(type).c_str()); + break; + default: + if (!ret) + return fail("Buffer type %s not allowed!\n", buftype2s(type).c_str()); + break; + } + } - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_SLICED_VBI_CAPTURE); + ret = testEnumFormatsType(node, V4L2_BUF_TYPE_PRIVATE); if (ret > 0) return ret; if (!ret) - return fail("Buffer type SLICED_VBI_CAPTURE allowed!\n"); + warn("Buffer type PRIVATE allowed!\n"); + + ret = testEnumFrameSizes(node, 0x20202020); + if (ret >= 0) + return fail("Accepted framesize for invalid format\n"); + ret = testEnumFrameIntervals(node, 0x20202020, 640, 480, false); + if (ret >= 0) + return fail("Accepted frameinterval for invalid format\n"); + return 0; +} - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VIDEO_OUTPUT); - if (ret > 0) - return ret; - if (ret && (node->caps & V4L2_CAP_VIDEO_OUTPUT)) - return fail("Video output cap set, but no output formats defined\n"); - if (!ret && !(node->caps & V4L2_CAP_VIDEO_OUTPUT)) - return fail("Video output cap not set, but output formats defined\n"); +static int testFormatsType(struct node *node, enum v4l2_buf_type type) +{ + pixfmt_set &set = node->buftype_pixfmts[type]; + pixfmt_set *set_splane; + struct v4l2_format fmt; + struct v4l2_pix_format &pix = fmt.fmt.pix; + struct v4l2_pix_format_mplane &pix_mp = fmt.fmt.pix_mp; + struct v4l2_window &win = fmt.fmt.win; + struct v4l2_vbi_format &vbi = fmt.fmt.vbi; + struct v4l2_sliced_vbi_format &sliced = fmt.fmt.sliced; + __u32 service_set = 0; + unsigned cnt = 0; + int ret; + + memset(&fmt, 0xff, sizeof(fmt)); + fmt.type = type; + ret = doioctl(node, VIDIOC_G_FMT, &fmt); + if (ret == EINVAL) + return -ENOSYS; + if (ret) + return fail("expected EINVAL, but got %d when getting format for buftype %d\n", ret, type); + fail_on_test(fmt.type != type); + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + fail_on_test(!pix.width || !pix.height); + if (set.find(pix.pixelformat) == set.end()) + return fail("unknown pixelformat %08x for buftype %d\n", + pix.pixelformat, type); + fail_on_test(pix.bytesperline && pix.bytesperline < pix.width); + fail_on_test(!pix.sizeimage); + fail_on_test(!pix.colorspace); + if (pix.priv) + warn("priv is non-zero!\n"); + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + fail_on_test(!pix_mp.width || !pix_mp.height); + set_splane = &node->buftype_pixfmts[type - 8]; + if (set.find(pix_mp.pixelformat) == set.end() && + set_splane->find(pix_mp.pixelformat) == set_splane->end()) + return fail("unknown pixelformat %08x for buftype %d\n", + pix_mp.pixelformat, type); + fail_on_test(!pix_mp.colorspace); + ret = check_0(pix_mp.reserved, sizeof(pix_mp.reserved)); + if (ret) + return fail("pix_mp.reserved not zeroed\n"); + fail_on_test(pix_mp.num_planes == 0 || pix_mp.num_planes >= VIDEO_MAX_PLANES); + for (int i = 0; i < pix_mp.num_planes; i++) { + struct v4l2_plane_pix_format &pfmt = pix_mp.plane_fmt[i]; - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (ret > 0) - return ret; - if (ret && (node->caps & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) - return fail("MPlane Video output cap set, but no multiplanar output formats defined\n"); - if (!ret && !(node->caps & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) - return fail("MPlane Video output cap not set, but multiplanar output formats defined\n"); + ret = check_0(pfmt.reserved, sizeof(pfmt.reserved)); + if (ret) + return fail("pix_mp.plane_fmt[%d].reserved not zeroed\n", i); + fail_on_test(!pfmt.sizeimage); + fail_on_test(pfmt.bytesperline && pfmt.bytesperline < pix_mp.width); + } + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + fail_on_test(!vbi.sampling_rate); + fail_on_test(!vbi.samples_per_line); + fail_on_test(vbi.sample_format != V4L2_PIX_FMT_GREY); + fail_on_test(vbi.offset > vbi.samples_per_line); + ret = check_0(vbi.reserved, sizeof(vbi.reserved)); + if (ret) + return fail("vbi.reserved not zeroed\n"); + fail_on_test(!vbi.count[0] || !vbi.count[1]); + fail_on_test(vbi.flags & ~(V4L2_VBI_UNSYNC | V4L2_VBI_INTERLACED)); + if (vbi.flags & V4L2_VBI_INTERLACED) + fail_on_test(vbi.count[0] != vbi.count[1]); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + ret = check_0(sliced.reserved, sizeof(sliced.reserved)); + if (ret) + return fail("sliced.reserved not zeroed\n"); + fail_on_test(sliced.service_lines[0][0] || sliced.service_lines[1][0]); + for (int f = 0; f < 2; f++) { + for (int i = 0; i < 24; i++) { + if (sliced.service_lines[f][i]) + cnt++; + service_set |= sliced.service_lines[f][i]; + } + } + fail_on_test(sliced.io_size < sizeof(struct v4l2_sliced_vbi_data) * cnt); + fail_on_test(sliced.service_set != service_set); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + fail_on_test(win.field != V4L2_FIELD_ANY && + win.field != V4L2_FIELD_TOP && + win.field != V4L2_FIELD_BOTTOM && + win.field != V4L2_FIELD_INTERLACED); + for (struct v4l2_clip *clip = win.clips; clip; win.clipcount--) { + fail_on_test(clip == NULL); + clip = clip->next; + } + fail_on_test(win.clipcount); + break; + case V4L2_BUF_TYPE_PRIVATE: + break; + } + return 0; +} - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY); - if (ret > 0) - return ret; - if (!ret) - return fail("Buffer type VIDEO_OUTPUT_OVERLAY allowed!\n"); +int testFormats(struct node *node) +{ + int type; + int ret; - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_VBI_OUTPUT); - if (ret > 0) - return ret; - if (!ret) - return fail("Buffer type VBI_OUTPUT allowed!\n"); + for (type = 0; type <= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; type++) { + ret = testFormatsType(node, (enum v4l2_buf_type)type); - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT); - if (ret > 0) - return ret; - if (!ret) - return fail("Buffer type SLICED_VBI_OUTPUT allowed!\n"); + if (ret > 0) + return ret; + if (ret && (node->caps & buftype2cap[type])) + return fail("%s cap set, but no %s formats defined\n", + buftype2s(type).c_str(), buftype2s(type).c_str()); + if (!ret && !(node->caps & buftype2cap[type])) + return fail("%s cap not set, but %s formats defined\n", + buftype2s(type).c_str(), buftype2s(type).c_str()); + } - ret = testEnumFormatsType(node, V4L2_BUF_TYPE_PRIVATE); + ret = testFormatsType(node, V4L2_BUF_TYPE_PRIVATE); if (ret > 0) return ret; if (!ret) warn("Buffer type PRIVATE allowed!\n"); - - ret = testEnumFrameSizes(node, 0x20202020); - if (ret >= 0) - return fail("Accepted framesize for invalid format\n"); - ret = testEnumFrameIntervals(node, 0x20202020, 640, 480, false); - if (ret >= 0) - return fail("Accepted frameinterval for invalid format\n"); return 0; } diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp index 227ce1a6..44cb1533 100644 --- a/utils/v4l2-ctl/v4l2-ctl.cpp +++ b/utils/v4l2-ctl/v4l2-ctl.cpp @@ -584,6 +584,8 @@ static std::string num2s(unsigned num) static std::string buftype2s(int type) { switch (type) { + case 0: + return "Invalid"; case V4L2_BUF_TYPE_VIDEO_CAPTURE: return "Video Capture"; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: -- 2.34.1