Add the ability to set and display video control values directly on the

CLI.

Initial diff from Laurence Tratt with some enhancements/changes added.

ok mpi@
This commit is contained in:
mglocker 2020-08-05 10:49:47 +00:00
parent 587f130e27
commit 4fa22cfd66
2 changed files with 209 additions and 80 deletions

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: video.1,v 1.15 2020/07/17 07:51:23 mglocker Exp $
.\" $OpenBSD: video.1,v 1.16 2020/08/05 10:49:47 mglocker Exp $
.\"
.\" Copyright (c) 2010 Jacob Meuser <jakemsr@openbsd.org>
.\"
@ -15,7 +15,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\"
.Dd $Mdocdate: July 17 2020 $
.Dd $Mdocdate: August 5 2020 $
.Dt VIDEO 1
.Os
.Sh NAME
@ -25,7 +25,7 @@
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl \&gqRv
.Op Fl \&cdgqRv
.Op Fl a Ar adaptor
.Op Fl e Ar encoding
.Op Fl f Ar file
@ -34,6 +34,7 @@
.Op Fl o Ar output
.Op Fl r Ar rate
.Op Fl s Ar size
.Op Ar control Ns Op = Ns Ar value
.Ek
.Sh DESCRIPTION
.Nm
@ -81,6 +82,10 @@ Index of
adaptor to use.
The default is 0, the first adaptor reported by
.Xr X 7 .
.It Fl c
List all current supported control values and quit.
.It Fl d
Reset all supported controls to their default value and quit.
.It Fl e Ar encoding
Lowercase FOURCC name of video encoding to use.
Valid arguments are
@ -219,6 +224,15 @@ Verbose mode.
Multiple instances of this option are allowed.
Each instance increases the level of informational output printed to
.Ar stderr .
.It Ar control Ns Op = Ns Ar value
Retrieve the specified
.Ar control ,
or attempt to set it to
.Ar value
and quit.
Multiple
.Ar control Ns Op = Ns Ar value
arguments may be given.
.El
.Pp
.Nm

View File

@ -1,4 +1,4 @@
/* $OpenBSD: video.c,v 1.31 2020/07/17 07:51:23 mglocker Exp $ */
/* $OpenBSD: video.c,v 1.32 2020/08/05 10:49:47 mglocker Exp $ */
/*
* Copyright (c) 2010 Jacob Meuser <jakemsr@openbsd.org>
*
@ -192,6 +192,8 @@ struct video {
#define M_IN_FILE 0x4
#define M_OUT_FILE 0x8
#define M_QUERY 0x10
#define M_QUERY_CTRLS 0x20
#define M_RESET 0x40
int mode;
int verbose;
};
@ -211,11 +213,14 @@ int dev_get_rates(struct video *);
int dev_get_ctrls(struct video *);
void dev_dump_info(struct video *);
void dev_dump_query(struct video *);
void dev_dump_query_ctrls(struct video *);
int dev_init(struct video *);
void dev_set_ctrl(struct video *, int, int);
void dev_set_ctrl_auto_white_balance(struct video *, int);
int dev_set_ctrl_abs(struct video *vid, int, int);
void dev_set_ctrl_rel(struct video *, int, int);
void dev_set_ctrl_auto_white_balance(struct video *, int, int);
void dev_reset_ctrls(struct video *);
int parse_ctrl(struct video *, int, char **);
int parse_size(struct video *);
int choose_size(struct video *);
int choose_enc(struct video *);
@ -240,10 +245,10 @@ extern char *__progname;
void
usage(void)
{
fprintf(stderr, "usage: %s [-gqRv] "
fprintf(stderr, "usage: %s [-cdgqRv] "
"[-a adaptor] [-e encoding] [-f file] [-i input] [-O output]\n"
" %*s [-o output] [-r rate] [-s size]\n", __progname,
(int)strlen(__progname), "");
" %*s [-o output] [-r rate] [-s size] [control[=value]]\n",
__progname, (int)strlen(__progname), "");
}
int
@ -657,46 +662,46 @@ display_event(struct video *vid)
switch (str) {
case 'A':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_SHARPNESS, 1);
dev_set_ctrl_rel(vid, CTRL_SHARPNESS, 1);
break;
case 'a':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_SHARPNESS, -1);
dev_set_ctrl_rel(vid, CTRL_SHARPNESS, -1);
break;
case 'B':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_BRIGHTNESS, 1);
dev_set_ctrl_rel(vid, CTRL_BRIGHTNESS, 1);
break;
case 'b':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_BRIGHTNESS, -1);
dev_set_ctrl_rel(vid, CTRL_BRIGHTNESS, -1);
break;
case 'C':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_CONTRAST, 1);
dev_set_ctrl_rel(vid, CTRL_CONTRAST, 1);
break;
case 'c':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_CONTRAST, -1);
dev_set_ctrl_rel(vid, CTRL_CONTRAST, -1);
break;
case 'f':
resize_window(vid, 1);
break;
case 'G':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_GAIN, 1);
dev_set_ctrl_rel(vid, CTRL_GAIN, 1);
break;
case 'g':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_GAIN, -1);
dev_set_ctrl_rel(vid, CTRL_GAIN, -1);
break;
case 'H':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_HUE, 1);
dev_set_ctrl_rel(vid, CTRL_HUE, 1);
break;
case 'h':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_HUE, -1);
dev_set_ctrl_rel(vid, CTRL_HUE, -1);
break;
case 'O':
if (!wout && vid->verbose > 0)
@ -710,11 +715,11 @@ display_event(struct video *vid)
break;
case 'M':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_GAMMA, 1);
dev_set_ctrl_rel(vid, CTRL_GAMMA, 1);
break;
case 'm':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_GAMMA, -1);
dev_set_ctrl_rel(vid, CTRL_GAMMA, -1);
break;
case 'p':
hold = !hold;
@ -728,20 +733,20 @@ display_event(struct video *vid)
break;
case 'S':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_SATURATION, 1);
dev_set_ctrl_rel(vid, CTRL_SATURATION, 1);
break;
case 's':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid, CTRL_SATURATION, -1);
dev_set_ctrl_rel(vid, CTRL_SATURATION, -1);
break;
case 'W':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid,
dev_set_ctrl_rel(vid,
CTRL_WHITE_BALANCE_TEMPERATURE, 10);
break;
case 'w':
if (vid->mode & M_IN_DEV)
dev_set_ctrl(vid,
dev_set_ctrl_rel(vid,
CTRL_WHITE_BALANCE_TEMPERATURE, -10);
break;
default:
@ -1009,11 +1014,55 @@ dev_get_ctrls(struct video *vid)
return 1;
}
void
dev_set_ctrl(struct video *vid, int ctrl, int change)
int
dev_set_ctrl_abs(struct video *vid, int ctrl, int val)
{
struct dev *d = &vid->dev;
struct v4l2_control control;
if (ctrl < 0 || ctrl >= CTRL_LAST) {
warnx("invalid control");
return -1;
}
if (!ctrls[ctrl].supported) {
warnx("control %s not supported by %s",
ctrls[ctrl].name, d->path);
return -1;
}
if (ctrl == CTRL_WHITE_BALANCE_TEMPERATURE) {
/*
* The spec requires auto-white balance to be off before
* we can set the white balance temperature.
*/
dev_set_ctrl_auto_white_balance(vid, 0, 0);
}
if (val > ctrls[ctrl].max)
val = ctrls[ctrl].max;
else if (val < ctrls[ctrl].min)
val = ctrls[ctrl].min;
control.id = ctrls[ctrl].id;
control.value = val;
if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0) {
warn("VIDIOC_S_CTRL");
return -1;
}
control.id = ctrls[ctrl].id;
if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0) {
warn("VIDIOC_G_CTRL");
return -1;
}
ctrls[ctrl].cur = control.value;
if (vid->verbose > 0)
fprintf(stderr, "%s now %d\n", ctrls[ctrl].name,
ctrls[ctrl].cur);
return 0;
}
void
dev_set_ctrl_rel(struct video *vid, int ctrl, int change)
{
struct dev *d = &vid->dev;
int val;
if (ctrl < 0 || ctrl >= CTRL_LAST) {
@ -1025,50 +1074,32 @@ dev_set_ctrl(struct video *vid, int ctrl, int change)
ctrls[ctrl].name, d->path);
return;
}
if (ctrl == CTRL_WHITE_BALANCE_TEMPERATURE) {
/*
* The spec requires auto-white balance to be off before
* we can set the white balance temperature.
*/
dev_set_ctrl_auto_white_balance(vid, 0);
}
val = ctrls[ctrl].cur + ctrls[ctrl].step * change;
if (val > ctrls[ctrl].max)
val = ctrls[ctrl].max;
else if (val < ctrls[ctrl].min)
val = ctrls[ctrl].min;
control.id = ctrls[ctrl].id;
control.value = val;
if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0) {
warn("VIDIOC_S_CTRL");
return;
}
control.id = ctrls[ctrl].id;
if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0) {
warn("VIDIOC_G_CTRL");
return;
}
ctrls[ctrl].cur = control.value;
if (vid->verbose > 0)
fprintf(stderr, "%s now %d\n", ctrls[ctrl].name,
ctrls[ctrl].cur);
dev_set_ctrl_abs(vid, ctrl, val);
}
void
dev_set_ctrl_auto_white_balance(struct video *vid, int toggle)
dev_set_ctrl_auto_white_balance(struct video *vid, int value, int reset)
{
struct dev *d = &vid->dev;
struct v4l2_control control;
control.id = V4L2_CID_AUTO_WHITE_BALANCE;
if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0)
if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0) {
warn("VIDIOC_G_CTRL");
if (control.value == toggle)
return;
}
control.value = toggle;
if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0)
warn("VIDIOC_S_CTRL");
if (reset) {
if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0)
warn("VIDIOC_S_CTRL");
} else {
if (control.value == value)
return;
control.value = value;
if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0)
warn("VIDIOC_S_CTRL");
}
}
void
@ -1081,27 +1112,9 @@ dev_reset_ctrls(struct video *vid)
for (i = 0; i < CTRL_LAST; i++) {
if (!ctrls[i].supported)
continue;
dev_set_ctrl_abs(vid, i, ctrls[i].def);
if (i == CTRL_WHITE_BALANCE_TEMPERATURE) {
/*
* We might be asked to reset before the white balance
* temperature has been adjusted, so we need to make
* sure that auto-white balance really is off.
*/
dev_set_ctrl_auto_white_balance(vid, 0);
}
control.id = ctrls[i].id;
control.value = ctrls[i].def;
if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0)
warn("VIDIOC_S_CTRL(%s)", ctrls[i].name);
control.id = ctrls[i].id;
if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0)
warn("VIDIOC_G_CTRL(%s)", ctrls[i].name);
ctrls[i].cur = control.value;
if (vid->verbose > 0)
fprintf(stderr, "%s now %d\n", ctrls[i].name,
ctrls[i].cur);
if (i == CTRL_WHITE_BALANCE_TEMPERATURE) {
dev_set_ctrl_auto_white_balance(vid, 1);
dev_set_ctrl_auto_white_balance(vid, 1, 0);
}
}
}
@ -1171,6 +1184,22 @@ dev_dump_query(struct video *vid)
dev_dump_info(vid);
}
void
dev_dump_query_ctrls(struct video *vid)
{
int i;
if (!dev_check_caps(vid))
return;
if (!dev_get_ctrls(vid))
return;
for (i = 0; i < CTRL_LAST; i++) {
if (ctrls[i].supported)
fprintf(stderr, "%s=%d\n", ctrls[i].name, ctrls[i].cur);
}
}
int
dev_init(struct video *vid)
{
@ -1221,6 +1250,64 @@ dev_init(struct video *vid)
return 1;
}
int
parse_ctrl(struct video *vid, int argc, char **argv)
{
int i, val_old, val_new;
char *p;
const char *errstr;
if (*argv == NULL)
return 1; /* No control arguments found. */
if (!dev_check_caps(vid))
return 0;
if (!dev_get_ctrls(vid))
return 0;
for (; argc > 0; argc--, argv++) {
p = strchr(*argv, '=');
/* Display control value. */
if (p == NULL) {
for (i = 0; i < CTRL_LAST; i++) {
if (!strcmp(*argv, ctrls[i].name)) {
fprintf(stderr, "%s: %d\n",
ctrls[i].name, ctrls[i].cur);
break;
}
}
if (i == CTRL_LAST)
warnx("%s: unknown control", *argv);
continue;
}
/* Set control value. */
for (i = 0, *p++ = '\0'; i < CTRL_LAST; i++) {
if (strcmp(*argv, ctrls[i].name) != 0)
continue;
if (*p == '\0') {
warnx("%s: no value", *argv);
break;
}
val_new = strtonum(p, -32768, 32768, &errstr);
if (errstr != NULL) {
warnx("%s: %s", *argv, errstr);
return 0;
}
val_old = ctrls[i].cur;
if (dev_set_ctrl_abs(vid, i, val_new) == 0)
fprintf(stderr, "%s: %d -> %d\n",
ctrls[i].name, val_old, ctrls[i].cur);
break;
}
if (i == CTRL_LAST)
warnx("%s: unknown control", *argv);
}
return 0;
}
int
parse_size(struct video *vid)
{
@ -1564,6 +1651,13 @@ setup(struct video *vid)
return 0;
}
/*
* Reset the current White Balance Temperature Auto Control value
* after the video stream has been started since some cams only
* process this control while the video stream is on.
*/
dev_set_ctrl_auto_white_balance(vid, 0, 1);
if (vid->mode & M_OUT_XV)
net_wm_supported(vid);
@ -1949,7 +2043,7 @@ main(int argc, char *argv[])
vid.mmap_on = 1; /* mmap method is default */
wout = 1;
while ((ch = getopt(argc, argv, "gqRva:e:f:i:O:o:r:s:")) != -1) {
while ((ch = getopt(argc, argv, "cdgqRva:e:f:i:O:o:r:s:")) != -1) {
switch (ch) {
case 'a':
x->cur_adap = strtonum(optarg, 0, 4, &errstr);
@ -1958,6 +2052,14 @@ main(int argc, char *argv[])
errs++;
}
break;
case 'c':
vid.mode |= M_QUERY_CTRLS;
vid.mode &= ~M_OUT_XV;
break;
case 'd':
vid.mode |= M_RESET;
vid.mode &= ~M_OUT_XV;
break;
case 'e':
vid.enc = find_enc(optarg);
if (vid.enc >= ENC_LAST) {
@ -2043,6 +2145,14 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
if (!parse_ctrl(&vid, argc, argv))
cleanup(&vid, 0);
if (vid.mode & M_QUERY_CTRLS) {
dev_dump_query_ctrls(&vid);
cleanup(&vid, 0);
}
if (vid.mode & M_QUERY) {
if (pledge("stdio rpath wpath video", NULL) == -1)
err(1, "pledge");
@ -2056,6 +2166,11 @@ main(int argc, char *argv[])
if (!setup(&vid))
cleanup(&vid, 1);
if (vid.mode & M_RESET) {
dev_reset_ctrls(&vid);
cleanup(&vid, 0);
}
if (vid.mode & M_IN_FILE) {
if (pledge("stdio rpath", NULL) == -1)
err(1, "pledge");