v4l2: support querying controls

This commit is contained in:
Dmitry Sharshakov 2022-11-21 20:09:03 +03:00 committed by Wim Taymans
parent 5c96928d12
commit 08107f1a02

View file

@ -2026,6 +2026,188 @@ exit_unlock:
return res;
}
// Copied from spa/plugins/v4l2/v4l2-utils.c
// TODO: unify with source
static struct {
uint32_t v4l2_id;
uint32_t spa_id;
} control_map[] = {
{ V4L2_CID_BRIGHTNESS, SPA_PROP_brightness },
{ V4L2_CID_CONTRAST, SPA_PROP_contrast },
{ V4L2_CID_SATURATION, SPA_PROP_saturation },
{ V4L2_CID_HUE, SPA_PROP_hue },
{ V4L2_CID_GAMMA, SPA_PROP_gamma },
{ V4L2_CID_EXPOSURE, SPA_PROP_exposure },
{ V4L2_CID_GAIN, SPA_PROP_gain },
{ V4L2_CID_SHARPNESS, SPA_PROP_sharpness },
};
static uint32_t prop_id_to_control(uint32_t prop_id)
{
SPA_FOR_EACH_ELEMENT_VAR(control_map, c) {
if (c->spa_id == prop_id)
return c->v4l2_id;
}
if (prop_id >= SPA_PROP_START_CUSTOM)
return prop_id - SPA_PROP_START_CUSTOM;
return SPA_ID_INVALID;
}
static int vidioc_queryctrl(struct file *file, struct v4l2_queryctrl *arg)
{
struct param *p;
bool found = false, next = false;
memset(arg->reserved, 0, sizeof(arg->reserved));
// TODO: V4L2_CTRL_FLAG_NEXT_COMPOUND
if (arg->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
pw_log_debug("VIDIOC_QUERYCTRL: 0x%08" PRIx32 " | V4L2_CTRL_FLAG_NEXT_CTRL", arg->id);
arg->id = arg->id & ~V4L2_CTRL_FLAG_NEXT_CTRL & ~V4L2_CTRL_FLAG_NEXT_COMPOUND;
next = true;
}
pw_log_debug("VIDIOC_QUERYCTRL: 0x%08" PRIx32, arg->id);
if (file->node == NULL)
return -EIO;
pw_thread_loop_lock(file->loop);
// FIXME: place found item into a variable. Will fix unordered ctrls
spa_list_for_each(p, &file->node->param_list, link) {
uint32_t prop_id, ctrl_id, n_vals, choice = SPA_ID_INVALID;
const char *prop_description;
const struct spa_pod *type, *pod;
if (p->id != SPA_PARAM_PropInfo || p->param == NULL)
continue;
if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_PropInfo, NULL,
SPA_PROP_INFO_id, SPA_POD_Id(&prop_id),
SPA_PROP_INFO_description, SPA_POD_String(&prop_description)) < 0)
continue;
if ((ctrl_id = prop_id_to_control(prop_id)) == SPA_ID_INVALID)
continue;
if ((next && ctrl_id > arg->id) || (!next && ctrl_id == arg->id)) {
if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_PropInfo, NULL,
SPA_PROP_INFO_type, SPA_POD_PodChoice(&type)) < 0)
continue;
// TODO: support setting controls
arg->flags = V4L2_CTRL_FLAG_READ_ONLY;
spa_scnprintf((char*)arg->name, sizeof(arg->name), "%s", prop_description);
// check type and populate range
pod = spa_pod_get_values(type, &n_vals, &choice);
if (spa_pod_is_int(pod)) {
if (n_vals < 4)
break;
arg->type = V4L2_CTRL_TYPE_INTEGER;
int *v = SPA_POD_BODY(pod);
arg->default_value = v[0];
arg->minimum = v[1];
arg->maximum = v[2];
arg->step = v[3];
}
else if (spa_pod_is_bool(pod) && n_vals > 0) {
arg->type = V4L2_CTRL_TYPE_BOOLEAN;
arg->default_value = SPA_POD_VALUE(struct spa_pod_bool, pod);
arg->minimum = 0;
arg->maximum = 1;
arg->step = 1;
}
else {
break;
}
arg->id = ctrl_id;
found = true;
pw_log_debug("ctrl 0x%08" PRIx32 " ok", arg->id);
break;
}
}
pw_thread_loop_unlock(file->loop);
if (!found) {
pw_log_info("not found ctrl 0x%08" PRIx32, arg->id);
return -EINVAL;
}
return 0;
}
static int vidioc_g_ctrl(struct file *file, struct v4l2_control *arg)
{
struct param *p;
bool found = false;
pw_log_debug("VIDIOC_G_CTRL: 0x%08" PRIx32, arg->id);
if (file->node == NULL)
return -EIO;
pw_thread_loop_lock(file->loop);
spa_list_for_each(p, &file->node->param_list, link) {
uint32_t prop_id, ctrl_id, n_vals, choice = SPA_ID_INVALID;
const char *prop_description;
const struct spa_pod *type, *pod;
if (p->id != SPA_PARAM_PropInfo || p->param == NULL)
continue;
if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_PropInfo, NULL,
SPA_PROP_INFO_id, SPA_POD_Id(&prop_id),
SPA_PROP_INFO_description, SPA_POD_String(&prop_description)) < 0)
continue;
if ((ctrl_id = prop_id_to_control(prop_id)) == SPA_ID_INVALID)
continue;
if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_PropInfo, NULL,
SPA_PROP_INFO_type, SPA_POD_PodChoice(&type)) < 0)
continue;
if (ctrl_id == arg->id) {
// TODO: support getting true ctrl values instead of defaults
pod = spa_pod_get_values(type, &n_vals, &choice);
if (spa_pod_is_int(pod)) {
if (n_vals < 4)
break;
int *v = SPA_POD_BODY(pod);
arg->value = v[0];
}
else if (spa_pod_is_bool(pod) && n_vals > 0) {
arg->value = SPA_POD_VALUE(struct spa_pod_bool, pod);
}
else {
break;
}
found = true;
pw_log_debug("ctrl 0x%08" PRIx32 " ok", arg->id);
break;
}
}
pw_thread_loop_unlock(file->loop);
if (!found) {
pw_log_info("not found ctrl 0x%08" PRIx32, arg->id);
return -EINVAL;
}
return 0;
}
static int v4l2_ioctl(int fd, unsigned long int request, void *arg)
{
int res;
@ -2099,6 +2281,13 @@ static int v4l2_ioctl(int fd, unsigned long int request, void *arg)
case VIDIOC_STREAMOFF:
res = vidioc_streamoff(file, (int *)arg);
break;
// TODO: VIDIOC_QUERY_EXT_CTRL
case VIDIOC_QUERYCTRL:
res = vidioc_queryctrl(file, (struct v4l2_queryctrl *)arg);
break;
case VIDIOC_G_CTRL:
res = vidioc_g_ctrl(file, (struct v4l2_control *)arg);
break;
default:
res = -ENOTTY;
break;