From c8f31f415f0bf6f4a4b180c145759eca4948ea2d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 16:10:17 +0200 Subject: [PATCH] v4l2-ctl: add support for array controls Signed-off-by: Hans Verkuil --- utils/v4l2-ctl/v4l2-ctl-common.cpp | 275 ++++++++++++++++++++++++++++--------- 1 file changed, 213 insertions(+), 62 deletions(-) diff --git a/utils/v4l2-ctl/v4l2-ctl-common.cpp b/utils/v4l2-ctl/v4l2-ctl-common.cpp index f302eaa..bc04bc9 100644 --- a/utils/v4l2-ctl/v4l2-ctl-common.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-common.cpp @@ -27,7 +27,7 @@ typedef std::map > class2ctrls_map; -typedef std::map ctrl_qmap; +typedef std::map ctrl_qmap; static ctrl_qmap ctrl_str2q; typedef std::map ctrl_idmap; static ctrl_idmap ctrl_id2str; @@ -194,7 +194,7 @@ static void list_devices() } } -static std::string name2var(unsigned char *name) +static std::string name2var(char *name) { std::string s; int add_underscore = 0; @@ -260,63 +260,105 @@ static std::string ctrlflags2s(__u32 flags) { V4L2_CTRL_FLAG_SLIDER, "slider" }, { V4L2_CTRL_FLAG_WRITE_ONLY, "write-only" }, { V4L2_CTRL_FLAG_VOLATILE, "volatile" }, + { V4L2_CTRL_FLAG_HAS_PAYLOAD,"has-payload" }, { 0, NULL } }; return flags2s(flags, def); } -static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, +static void print_qctrl(int fd, struct v4l2_query_ext_ctrl *queryctrl, struct v4l2_ext_control *ctrl, int show_menus) { struct v4l2_querymenu qmenu; std::string s = name2var(queryctrl->name); - int i; + unsigned i; memset(&qmenu, 0, sizeof(qmenu)); qmenu.id = queryctrl->id; switch (queryctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - printf("%31s (int) : min=%d max=%d step=%d default=%d value=%d", + printf("%31s (int) : min=%lld max=%lld step=%lld default=%lld", s.c_str(), queryctrl->minimum, queryctrl->maximum, - queryctrl->step, queryctrl->default_value, - ctrl->value); + queryctrl->step, queryctrl->default_value); break; case V4L2_CTRL_TYPE_INTEGER64: - printf("%31s (int64) : value=%lld", s.c_str(), ctrl->value64); + printf("%31s (int64) : min=%lld max=%lld step=%lld default=%lld", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, queryctrl->default_value); break; case V4L2_CTRL_TYPE_STRING: - printf("%31s (str) : min=%d max=%d step=%d value='%s'", + printf("%31s (str) : min=%lld max=%lld step=%lld", s.c_str(), queryctrl->minimum, queryctrl->maximum, - queryctrl->step, safename(ctrl->string).c_str()); + queryctrl->step); break; case V4L2_CTRL_TYPE_BOOLEAN: - printf("%31s (bool) : default=%d value=%d", - s.c_str(), - queryctrl->default_value, ctrl->value); + printf("%31s (bool) : default=%lld", + s.c_str(), queryctrl->default_value); break; case V4L2_CTRL_TYPE_MENU: - printf("%31s (menu) : min=%d max=%d default=%d value=%d", + printf("%31s (menu) : min=%lld max=%lld default=%lld", s.c_str(), queryctrl->minimum, queryctrl->maximum, - queryctrl->default_value, ctrl->value); + queryctrl->default_value); break; case V4L2_CTRL_TYPE_INTEGER_MENU: - printf("%31s (intmenu): min=%d max=%d default=%d value=%d", + printf("%31s (intmenu): min=%lld max=%lld default=%lld", s.c_str(), queryctrl->minimum, queryctrl->maximum, - queryctrl->default_value, ctrl->value); + queryctrl->default_value); break; case V4L2_CTRL_TYPE_BUTTON: printf("%31s (button) :", s.c_str()); break; case V4L2_CTRL_TYPE_BITMASK: - printf("%31s (bitmask): max=0x%08x default=0x%08x value=0x%08x", + printf("%31s (bitmask): max=0x%08llx default=0x%08llx", s.c_str(), queryctrl->maximum, - queryctrl->default_value, ctrl->value); + queryctrl->default_value); + break; + case V4L2_CTRL_TYPE_U8: + printf("%31s (u8) : min=%lld max=%lld step=%lld default=%lld", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, queryctrl->default_value); + break; + case V4L2_CTRL_TYPE_U16: + printf("%31s (u16) : min=%lld max=%lld step=%lld default=%lld", + s.c_str(), + queryctrl->minimum, queryctrl->maximum, + queryctrl->step, queryctrl->default_value); break; - default: break; + default: + printf("%31s (unknown): type=%x", s.c_str(), queryctrl->type); + break; + } + if (queryctrl->nr_of_dims == 0) { + switch (queryctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + printf(" value=%d", ctrl->value); + break; + case V4L2_CTRL_TYPE_BITMASK: + printf(" value=0x%08x", ctrl->value); + break; + case V4L2_CTRL_TYPE_INTEGER64: + printf(" value=%lld", ctrl->value64); + break; + case V4L2_CTRL_TYPE_STRING: + printf(" value='%s'", safename(ctrl->string).c_str()); + break; + default: + break; + } + } + if (queryctrl->nr_of_dims) { + printf(" "); + for (i = 0; i < queryctrl->nr_of_dims; i++) + printf("[%u]", queryctrl->dims[i]); } if (queryctrl->flags) printf(" flags=%s", ctrlflags2s(queryctrl->flags).c_str()); @@ -335,7 +377,7 @@ static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, } } -static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) +static int print_control(int fd, struct v4l2_query_ext_ctrl &qctrl, int show_menus) { struct v4l2_control ctrl; struct v4l2_ext_control ext_ctrl; @@ -356,6 +398,10 @@ static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) print_qctrl(fd, &qctrl, &ext_ctrl, show_menus); return 1; } + if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES) { + print_qctrl(fd, &qctrl, NULL, show_menus); + return 1; + } ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(qctrl.id); ctrls.count = 1; ctrls.controls = &ext_ctrl; @@ -389,57 +435,97 @@ static int print_control(int fd, struct v4l2_queryctrl &qctrl, int show_menus) return 1; } +static int query_ext_ctrl_ioctl(int fd, struct v4l2_query_ext_ctrl &qctrl) +{ + struct v4l2_queryctrl qc; + int rc; + + rc = test_ioctl(fd, VIDIOC_QUERY_EXT_CTRL, &qctrl); + if (errno != ENOTTY) + return rc; + qc.id = qctrl.id; + rc = test_ioctl(fd, VIDIOC_QUERYCTRL, &qc); + if (rc == 0) { + qctrl.type = qc.type; + memcpy(qctrl.name, qc.name, sizeof(qctrl.name)); + qctrl.minimum = qc.minimum; + qctrl.maximum = qc.maximum; + qctrl.step = qc.step; + qctrl.default_value = qc.default_value; + qctrl.flags = qc.flags; + qctrl.elems = 1; + qctrl.nr_of_dims = 0; + memset(qctrl.dims, 0, sizeof(qctrl.dims)); + switch (qctrl.type) { + case V4L2_CTRL_TYPE_INTEGER64: + qctrl.elem_size = sizeof(__s64); + break; + case V4L2_CTRL_TYPE_STRING: + qctrl.elem_size = qc.maximum + 1; + break; + default: + qctrl.elem_size = sizeof(__s32); + break; + } + memset(qctrl.reserved, 0, sizeof(qctrl.reserved)); + } + qctrl.id = qc.id; + return rc; +} + static void list_controls(int fd, int show_menus) { - struct v4l2_queryctrl qctrl; + const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; + struct v4l2_query_ext_ctrl qctrl; int id; memset(&qctrl, 0, sizeof(qctrl)); - qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; - while (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { - print_control(fd, qctrl, show_menus); - qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + qctrl.id = next_fl; + while (query_ext_ctrl_ioctl(fd, qctrl) == 0) { + print_control(fd, qctrl, show_menus); + qctrl.id |= next_fl; } - if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + if (!(qctrl.id & next_fl)) return; for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { qctrl.id = id; - if (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) + if (query_ext_ctrl_ioctl(fd, qctrl) == 0) print_control(fd, qctrl, show_menus); } for (qctrl.id = V4L2_CID_PRIVATE_BASE; - test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + query_ext_ctrl_ioctl(fd, qctrl) == 0; qctrl.id++) { print_control(fd, qctrl, show_menus); } } static void find_controls(int fd) { - struct v4l2_queryctrl qctrl; + const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; + struct v4l2_query_ext_ctrl qctrl; int id; memset(&qctrl, 0, sizeof(qctrl)); - qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; - while (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) { + qctrl.id = next_fl; + while (query_ext_ctrl_ioctl(fd, qctrl) == 0) { if (qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS && !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl_str2q[name2var(qctrl.name)] = qctrl; ctrl_id2str[qctrl.id] = name2var(qctrl.name); } - qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; + qctrl.id |= next_fl; } - if (qctrl.id != V4L2_CTRL_FLAG_NEXT_CTRL) + if (!(qctrl.id & next_fl)) return; for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) { qctrl.id = id; - if (test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0 && + if (query_ext_ctrl_ioctl(fd, qctrl) == 0 && !(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl_str2q[name2var(qctrl.name)] = qctrl; ctrl_id2str[qctrl.id] = name2var(qctrl.name); } } for (qctrl.id = V4L2_CID_PRIVATE_BASE; - test_ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) { + query_ext_ctrl_ioctl(fd, qctrl) == 0; qctrl.id++) { if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl_str2q[name2var(qctrl.name)] = qctrl; ctrl_id2str[qctrl.id] = name2var(qctrl.name); @@ -568,24 +654,39 @@ void common_set(int fd) for (ctrl_set_map::iterator iter = set_ctrls.begin(); iter != set_ctrls.end(); ++iter) { struct v4l2_ext_control ctrl; + struct v4l2_query_ext_ctrl &qc = ctrl_str2q[iter->first]; memset(&ctrl, 0, sizeof(ctrl)); - ctrl.id = ctrl_str2q[iter->first].id; - if (ctrl_str2q[iter->first].type == V4L2_CTRL_TYPE_INTEGER64) + ctrl.id = qc.id; + if (qc.type == V4L2_CTRL_TYPE_INTEGER64) use_ext_ctrls = true; - if (ctrl_str2q[iter->first].type == V4L2_CTRL_TYPE_STRING) { - unsigned len = iter->second.length(); - unsigned maxlen = ctrl_str2q[iter->first].maximum; + if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) { + long long v; + unsigned i; use_ext_ctrls = true; - ctrl.size = maxlen + 1; - ctrl.string = (char *)malloc(ctrl.size); - if (len > maxlen) { - memcpy(ctrl.string, iter->second.c_str(), maxlen); - ctrl.string[maxlen] = 0; - } - else { - strcpy(ctrl.string, iter->second.c_str()); + ctrl.size = qc.elems * qc.elem_size; + ctrl.ptr = malloc(ctrl.size); + + switch (qc.type) { + case V4L2_CTRL_TYPE_U8: + v = strtoul(iter->second.c_str(), NULL, 0); + for (i = 0; i < qc.elems; i++) + ctrl.p_u8[i] = v; + break; + case V4L2_CTRL_TYPE_U16: + v = strtoul(iter->second.c_str(), NULL, 0); + for (i = 0; i < qc.elems; i++) + ctrl.p_u16[i] = v; + break; + case V4L2_CTRL_TYPE_STRING: + strncpy(ctrl.string, iter->second.c_str(), qc.maximum); + ctrl.string[qc.maximum] = 0; + break; + default: + fprintf(stderr, "%s: unsupported payload type\n", + qc.name); + break; } } else { if (V4L2_CTRL_DRIVER_PRIV(ctrl.id)) @@ -618,7 +719,7 @@ void common_set(int fd) ctrls.controls = &iter->second[0]; if (doioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls)) { if (ctrls.error_idx >= ctrls.count) { - fprintf(stderr, "Error setting MPEG controls: %s\n", + fprintf(stderr, "Error setting controls: %s\n", strerror(errno)); } else { @@ -632,6 +733,42 @@ void common_set(int fd) } } +static void print_array(const struct v4l2_query_ext_ctrl &qc, void *p) +{ + unsigned divide[V4L2_CTRL_MAX_DIMS] = { 0 }; + unsigned d, i; + + for (d = 0; d < qc.nr_of_dims; d++) { + divide[d] = qc.dims[d]; + for (i = 0; i < d; i++) + divide[i] *= divide[d]; + } + for (unsigned idx = 0; idx < qc.elems / qc.dims[qc.nr_of_dims - 1]; idx++) { + printf("%s", qc.name); + for (i = 0; i < qc.nr_of_dims - 1; i++) + printf("[%u]", (idx / divide[i]) % qc.dims[i]); + printf(": "); + switch (qc.type) { + case V4L2_CTRL_TYPE_U8: + for (i = 0; i < qc.dims[qc.nr_of_dims - 1]; i++) { + printf("%4d", ((__u8 *)p)[idx + i]); + if (i < qc.dims[qc.nr_of_dims - 1] - 1) + printf(", "); + } + printf("\n"); + break; + case V4L2_CTRL_TYPE_U16: + for (i = 0; i < qc.dims[qc.nr_of_dims - 1]; i++) { + printf("%6d", ((__u16 *)p)[idx + i]); + if (i < qc.dims[qc.nr_of_dims - 1] - 1) + printf(", "); + } + printf("\n"); + break; + } + } +} + void common_get(int fd) { if (options[OptGetCtrl] && !get_ctrls.empty()) { @@ -643,16 +780,16 @@ void common_get(int fd) for (ctrl_get_list::iterator iter = get_ctrls.begin(); iter != get_ctrls.end(); ++iter) { struct v4l2_ext_control ctrl; + struct v4l2_query_ext_ctrl &qc = ctrl_str2q[*iter]; memset(&ctrl, 0, sizeof(ctrl)); - ctrl.id = ctrl_str2q[*iter].id; - if (ctrl_str2q[*iter].type == V4L2_CTRL_TYPE_INTEGER64) + ctrl.id = qc.id; + if (qc.type == V4L2_CTRL_TYPE_INTEGER64) use_ext_ctrls = true; - if (ctrl_str2q[*iter].type == V4L2_CTRL_TYPE_STRING) { + if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) { use_ext_ctrls = true; - ctrl.size = ctrl_str2q[*iter].maximum + 1; - ctrl.string = (char *)malloc(ctrl.size); - ctrl.string[0] = 0; + ctrl.size = qc.elems * qc.elem_size; + ctrl.ptr = calloc(1, ctrl.size); } if (V4L2_CTRL_DRIVER_PRIV(ctrl.id)) use_ext_ctrls = true; @@ -679,12 +816,26 @@ void common_get(int fd) doioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls); for (unsigned i = 0; i < iter->second.size(); i++) { struct v4l2_ext_control ctrl = iter->second[i]; - - if (ctrl_str2q[ctrl_id2str[ctrl.id]].type == V4L2_CTRL_TYPE_STRING) - printf("%s: '%s'\n", ctrl_id2str[ctrl.id].c_str(), - safename(ctrl.string).c_str()); - else - printf("%s: %d\n", ctrl_id2str[ctrl.id].c_str(), ctrl.value); + std::string &name = ctrl_id2str[ctrl.id]; + struct v4l2_query_ext_ctrl &qc = ctrl_str2q[name]; + + if (qc.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) { + switch (qc.type) { + case V4L2_CTRL_TYPE_U8: + case V4L2_CTRL_TYPE_U16: + print_array(qc, ctrl.ptr); + break; + case V4L2_CTRL_TYPE_STRING: + printf("%s: '%s'\n", name.c_str(), + safename(ctrl.string).c_str()); + break; + default: + fprintf(stderr, "%s: unsupported payload type\n", + qc.name); + break; + } + } else + printf("%s: %d\n", name.c_str(), ctrl.value); } } } -- 2.7.4