status: extend to provide all camera options and properties in JSON payload
- control: add `device_option_is_equal` - libcamera: provide human readable configurable options - v4l2: include camera control values - libcamera: store all applied controls - libcamera: use `device_option_is_equal`
This commit is contained in:
@ -13,7 +13,7 @@ int v4l2_device_set_option_by_id(device_t *dev, const char *name, uint32_t id, i
|
||||
|
||||
ctl.id = id;
|
||||
ctl.value = value;
|
||||
LOG_DEBUG(dev, "Configuring option %s (%08x) = %d", name, id, value);
|
||||
LOG_DEBUG(dev, "Configuring option '%s' (%08x) = %d", name, id, value);
|
||||
ERR_IOCTL(dev, dev->v4l2->subdev_fd >= 0 ? dev->v4l2->subdev_fd : dev->v4l2->dev_fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", name);
|
||||
return 0;
|
||||
error:
|
||||
@ -29,8 +29,6 @@ static int v4l2_device_query_control_iter_id(device_t *dev, int fd, uint32_t *id
|
||||
}
|
||||
*id = qctrl.id;
|
||||
|
||||
device_option_normalize_name(qctrl.name, qctrl.name);
|
||||
|
||||
if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
|
||||
LOG_VERBOSE(dev, "The '%s' is disabled", qctrl.name);
|
||||
return 0;
|
||||
@ -39,7 +37,7 @@ static int v4l2_device_query_control_iter_id(device_t *dev, int fd, uint32_t *id
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_VERBOSE(dev, "Available control: %s (%08x, type=%d)",
|
||||
LOG_VERBOSE(dev, "Available control: '%s' (%08x, type=%d)",
|
||||
qctrl.name, qctrl.id, qctrl.type);
|
||||
|
||||
dev->v4l2->controls = reallocarray(dev->v4l2->controls, dev->v4l2->ncontrols+1, sizeof(device_v4l2_control_t));
|
||||
@ -71,10 +69,8 @@ int v4l2_device_set_option(device_t *dev, const char *key, const char *value)
|
||||
device_v4l2_control_t *control = NULL;
|
||||
int ret = -1;
|
||||
|
||||
device_option_normalize_name(keyp, keyp);
|
||||
|
||||
for (int i = 0; i < dev->v4l2->ncontrols; i++) {
|
||||
if (strcmp(dev->v4l2->controls[i].control.name, keyp) == 0) {
|
||||
if (device_option_is_equal(dev->v4l2->controls[i].control.name, keyp)) {
|
||||
control = &dev->v4l2->controls[i];
|
||||
break;
|
||||
}
|
||||
@ -87,14 +83,40 @@ int v4l2_device_set_option(device_t *dev, const char *key, const char *value)
|
||||
|
||||
switch(control->control.type) {
|
||||
case V4L2_CTRL_TYPE_INTEGER:
|
||||
case V4L2_CTRL_TYPE_INTEGER_MENU:
|
||||
case V4L2_CTRL_TYPE_BOOLEAN:
|
||||
{
|
||||
struct v4l2_control ctl = {
|
||||
.id = control->control.id,
|
||||
.value = atoi(value)
|
||||
};
|
||||
LOG_INFO(dev, "Configuring option '%s' (%08x) = %d", control->control.name, ctl.id, ctl.value);
|
||||
ERR_IOCTL(dev, control->fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", control->control.name);
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
{
|
||||
struct v4l2_control ctl = {
|
||||
.id = control->control.id,
|
||||
.value = atoi(value)
|
||||
};
|
||||
LOG_INFO(dev, "Configuring option %s (%08x) = %d", control->control.name, ctl.id, ctl.value);
|
||||
|
||||
for (int j = control->control.minimum; j <= control->control.maximum; j++) {
|
||||
struct v4l2_querymenu menu = {
|
||||
.id = control->control.id,
|
||||
.index = j
|
||||
};
|
||||
|
||||
if (0 == ioctl(control->fd, VIDIOC_QUERYMENU, &menu)) {
|
||||
if (device_option_is_equal(valuep, (const char*)menu.name)) {
|
||||
ctl.value = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_INFO(dev, "Configuring option '%s' (%08x) = %d", control->control.name, ctl.id, ctl.value);
|
||||
ERR_IOCTL(dev, control->fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", control->control.name);
|
||||
ret = 1;
|
||||
}
|
||||
@ -138,7 +160,7 @@ int v4l2_device_set_option(device_t *dev, const char *key, const char *value)
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(dev, "Configuring option %s (%08x) = [%d tokens, expected %d]",
|
||||
LOG_INFO(dev, "Configuring option '%s' (%08x) = [%d tokens, expected %d]",
|
||||
control->control.name, ctl.id, tokens, control->control.elems);
|
||||
ERR_IOCTL(dev, control->fd, VIDIOC_S_EXT_CTRLS, &ctrls, "Can't set option %s", control->control.name);
|
||||
ret = 1;
|
||||
@ -163,8 +185,11 @@ void v4l2_device_dump_options(device_t *dev, FILE *stream)
|
||||
for (int i = 0; i < dev->v4l2->ncontrols; i++) {
|
||||
device_v4l2_control_t *control = &dev->v4l2->controls[i];
|
||||
|
||||
char name[512];
|
||||
device_option_normalize_name(control->control.name, name);
|
||||
|
||||
fprintf(stream, "- available option: %s (%08x, type=%d): ",
|
||||
control->control.name, control->control.id, control->control.type);
|
||||
name, control->control.id, control->control.type);
|
||||
|
||||
switch(control->control.type) {
|
||||
case V4L2_CTRL_TYPE_U8:
|
||||
@ -210,3 +235,163 @@ void v4l2_device_dump_options(device_t *dev, FILE *stream)
|
||||
}
|
||||
fprintf(stream, "\n");
|
||||
}
|
||||
|
||||
int v4l2_device_dump_options2(device_t *dev, device_option_fn fn, void *opaque)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < dev->v4l2->ncontrols; i++) {
|
||||
device_v4l2_control_t *control = &dev->v4l2->controls[i];
|
||||
|
||||
device_option_t opt = {
|
||||
.control_id = control->control.id,
|
||||
.elems = control->control.elems,
|
||||
};
|
||||
strcpy(opt.name, control->control.name);
|
||||
|
||||
char buf[8192];
|
||||
|
||||
struct v4l2_ext_control ext_control = {
|
||||
.id = control->control.id,
|
||||
.size = sizeof(buf),
|
||||
.ptr = buf,
|
||||
};
|
||||
|
||||
struct v4l2_ext_controls ext_controls = {
|
||||
.ctrl_class = 0,
|
||||
.which = V4L2_CTRL_WHICH_CUR_VAL,
|
||||
.count = 1,
|
||||
.controls = &ext_control
|
||||
};
|
||||
|
||||
bool ext_control_valid = ioctl(control->fd, VIDIOC_G_EXT_CTRLS, &ext_controls) == 0;
|
||||
|
||||
switch(control->control.type) {
|
||||
case V4L2_CTRL_TYPE_U8:
|
||||
opt.type = device_option_type_u8;
|
||||
snprintf(opt.description, sizeof(opt.description), "[%02x..%02x]",
|
||||
(__u8)control->control.minimum & 0xFF, (__u8)control->control.maximum & 0xFF);
|
||||
if (ext_control_valid) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < ext_control.size; i++) {
|
||||
if (n + 3 >= sizeof(opt.value)) break;
|
||||
if (i) opt.value[n++] = ' ';
|
||||
n += sprintf(opt.value + n, "%02x", ext_control.p_u8[i]&0xFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_U16:
|
||||
opt.type = device_option_type_u16;
|
||||
snprintf(opt.description, sizeof(opt.description), "[%04x..%04x]",
|
||||
(__u16)control->control.minimum & 0xFFFF, (__u16)control->control.maximum & 0xFFFF);
|
||||
if (ext_control_valid) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < ext_control.size; i++) {
|
||||
if (n + 5 >= sizeof(opt.value)) break;
|
||||
if (i) opt.value[n++] = ' ';
|
||||
n += sprintf(opt.value + n, "%04x", ext_control.p_u16[i]&0xFFFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_U32:
|
||||
opt.type = device_option_type_u32;
|
||||
snprintf(opt.description, sizeof(opt.description), "[%lld..%lld]",
|
||||
control->control.minimum, control->control.maximum);
|
||||
if (ext_control_valid) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < ext_control.size; i++) {
|
||||
if (n + 9 >= sizeof(opt.value)) break;
|
||||
if (i) opt.value[n++] = ' ';
|
||||
n += sprintf(opt.value + n, "%08x", ext_control.p_u32[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_BOOLEAN:
|
||||
opt.type = device_option_type_bool;
|
||||
snprintf(opt.description, sizeof(opt.description), "[%lld..%lld]",
|
||||
control->control.minimum, control->control.maximum);
|
||||
if (ext_control_valid)
|
||||
snprintf(opt.value, sizeof(opt.value), "%d", ext_control.value ? 1 : 0);
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_INTEGER:
|
||||
opt.type = device_option_type_integer;
|
||||
snprintf(opt.description, sizeof(opt.description), "[%lld..%lld]",
|
||||
control->control.minimum, control->control.maximum);
|
||||
if (ext_control_valid)
|
||||
snprintf(opt.value, sizeof(opt.value), "%d", ext_control.value);
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_INTEGER64:
|
||||
opt.type = device_option_type_integer64;
|
||||
snprintf(opt.description, sizeof(opt.description), "[%lld..%lld]",
|
||||
control->control.minimum, control->control.maximum);
|
||||
if (ext_control_valid)
|
||||
snprintf(opt.value, sizeof(opt.value), "%lld", ext_control.value64);
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_BUTTON:
|
||||
opt.type = device_option_type_bool;
|
||||
snprintf(opt.description, sizeof(opt.description), "button");
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
case V4L2_CTRL_TYPE_INTEGER_MENU:
|
||||
opt.type = device_option_type_integer;
|
||||
snprintf(opt.description, sizeof(opt.description), "[%lld..%lld]",
|
||||
control->control.minimum, control->control.maximum);
|
||||
|
||||
if (ext_control_valid)
|
||||
snprintf(opt.value, sizeof(opt.value), "%d", ext_control.value);
|
||||
|
||||
for (int j = control->control.minimum; j <= control->control.maximum; j++) {
|
||||
struct v4l2_querymenu menu = {
|
||||
.id = control->control.id,
|
||||
.index = j
|
||||
};
|
||||
|
||||
if (0 != ioctl(control->fd, VIDIOC_QUERYMENU, &menu))
|
||||
continue;
|
||||
|
||||
if (opt.menu_items >= MAX_DEVICE_OPTION_MENU) {
|
||||
opt.flags.invalid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
device_option_menu_t *opt_menu = &opt.menu[opt.menu_items++];
|
||||
opt_menu->id = j;
|
||||
|
||||
if (control->control.type == V4L2_CTRL_TYPE_MENU) {
|
||||
snprintf(opt_menu->name, sizeof(opt_menu->name), "%s", menu.name);
|
||||
if (ext_control_valid && opt_menu->id == ext_control.value)
|
||||
snprintf(opt.value, sizeof(opt.value), "%s", menu.name);
|
||||
} else {
|
||||
snprintf(opt_menu->name, sizeof(opt_menu->name), "%lld", menu.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_STRING:
|
||||
opt.type = device_option_type_string;
|
||||
if (ext_control_valid)
|
||||
snprintf(opt.value, sizeof(opt.value), "%s", ext_control.string);
|
||||
break;
|
||||
|
||||
default:
|
||||
opt.flags.invalid = true; // unsupported
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt.type) {
|
||||
int ret = fn(&opt, opaque);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ device_hw_t v4l2_device_hw = {
|
||||
.device_close = v4l2_device_close,
|
||||
.device_video_force_key = v4l2_device_video_force_key,
|
||||
.device_dump_options = v4l2_device_dump_options,
|
||||
.device_dump_options2 = v4l2_device_dump_options2,
|
||||
.device_set_fps = v4l2_device_set_fps,
|
||||
.device_set_option = v4l2_device_set_option,
|
||||
|
||||
|
@ -9,9 +9,12 @@
|
||||
|
||||
typedef struct buffer_s buffer_t;
|
||||
typedef struct buffer_list_s buffer_list_t;
|
||||
typedef struct device_option_s device_option_t;
|
||||
typedef struct device_s device_t;
|
||||
struct pollfd;
|
||||
|
||||
typedef int device_option_fn(device_option_t *option, void *opaque);
|
||||
|
||||
typedef struct device_v4l2_control_s {
|
||||
int fd;
|
||||
struct v4l2_query_ext_ctrl control;
|
||||
@ -38,6 +41,7 @@ int v4l2_device_open(device_t *dev);
|
||||
void v4l2_device_close(device_t *dev);
|
||||
int v4l2_device_video_force_key(device_t *dev);
|
||||
void v4l2_device_dump_options(device_t *dev, FILE *stream);
|
||||
int v4l2_device_dump_options2(device_t *dev, device_option_fn fn, void *opaque);
|
||||
int v4l2_device_set_fps(device_t *dev, int desired_fps);
|
||||
int v4l2_device_set_option(device_t *dev, const char *key, const char *value);
|
||||
|
||||
|
Reference in New Issue
Block a user